diff --git a/.eslintrc b/.eslintrc
new file mode 100644
index 00000000..d484158b
--- /dev/null
+++ b/.eslintrc
@@ -0,0 +1,36 @@
+{
+ "root": true,
+ "parser": "@babel/eslint-parser",
+ "parserOptions": {
+ "sourceType": "module"
+ },
+ "env": {
+ "es6": true,
+ "node": true,
+ "browser": true
+ },
+ "plugins": ["prettier"],
+ "extends": ["eslint:recommended", "plugin:prettier/recommended"],
+ "globals": {
+ "__namespace": false,
+ "__cmdOut":false,
+ "echarts": false,
+ "mapv": false,
+ "DC": false,
+ "Cesium": false
+ },
+ "rules": {
+ "global-require": 0,
+ "indent": 0,
+ "no-new": 0,
+ "camelcase": 0,
+ "padded-blocks": 0,
+ "no-unused-vars": 0,
+ "no-trailing-spaces": 0,
+ "no-mixed-spaces-and-tabs": 0,
+ "space-before-function-paren": [0, "always"],
+ "no-multiple-empty-lines": 0,
+ "no-prototype-builtins": 0,
+ "no-loss-of-precision":0
+ }
+}
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 64bc1a6b..5f9ad804 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -5,9 +5,9 @@ name: build
on:
push:
- branches: [ master ]
+ branches: [ gulp ]
pull_request:
- branches: [ master ]
+ branches: [ gulp ]
jobs:
build:
@@ -25,4 +25,4 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
- name: Publish project
- run: yarn && yarn run build
+ run: yarn && yarn run build:release
diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml
new file mode 100644
index 00000000..600761ec
--- /dev/null
+++ b/.github/workflows/dev.yml
@@ -0,0 +1,28 @@
+# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
+# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
+
+name: dev
+
+on:
+ push:
+ branches: [gulp]
+ pull_request:
+ branches: [gulp]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ strategy:
+ matrix:
+ node-version: [16.x]
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Use Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@v1
+ with:
+ node-version: ${{ matrix.node-version }}
+ - name: Build project
+ run: yarn && yarn build
+
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..28672490
--- /dev/null
+++ b/README.md
@@ -0,0 +1,222 @@
+# DC-SDK
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+[**🇨🇳 中文**](./README_zh.md) | [**🇬🇧English**](./README.md)
+
+`DC-SDK` is based on the open source project `Cesium` for the second development of two three-dimensional `WebGis` application framework , the framework optimizes the use of `Cesium` and adds some additional features , designed for developers to quickly build `WebGis` application.
+
+```warning
+Tips:This SDK is JS+GIS framework package. Developers need to have some front-end technology and GIS related technology
+```
+
+
+## Run examples
+
+```shell
+ yarn run build
+ yarn run server
+```
+
+## Installation
+
+`NPM / YARN` **_`(Recommend)`_**
+
+Installing with NPM or YARN is recommended and it works seamlessly with webpack.
+
+```shell
+yarn add @dvgis/dc-sdk
+-------------------------
+npm install @dvgis/dc-sdk
+```
+
+```js
+import DC from '@dvgis/dc-sdk/dist/dc.base.min'
+import DcCore from '@dvgis/dc-sdk/dist/dc.core.min'
+import DcChart from '@dvgis/dc-sdk/dist/dc.chart.min'
+import DcMapv from '@dvgis/dc-sdk/dist/dc.mapv.min'
+import DcS3M from '@dvgis/dc-sdk/dist/dc.s3m.min'
+import '@dvgis/dc-sdk/dist/dc.core.min.css'
+```
+
+`NPM / YARN` **_`(On-demand)`_**
+
+```shell
+yarn add @dvgis/dc-base
+yarn add @dvgis/dc-core
+yarn add @dvgis/dc-chart
+yarn add @dvgis/dc-mapv
+yarn add @dvgis/dc-s3m
+-------------------------
+npm install @dvgis/dc-base
+npm install @dvgis/dc-core
+npm install @dvgis/dc-chart
+npm install @dvgis/dc-mapv
+npm install @dvgis/dc-s3m
+```
+
+```js
+import DC from '@dvgis/dc-base'
+import DcCore from '@dvgis/dc-core'
+import DcChart from '@dvgis/dc-chart'
+import DcMapv from '@dvgis/dc-mapv'
+import DcS3M from '@dvgis/dc-s3m'
+import '@dvgis/dc-core/dist/dc.core.min.css'
+```
+
+`CDN`
+
+[Resources](https://github.com/dvgis/dc-sdk/releases)
+
+```html
+
+
+
+
+
+
+```
+
+```
+Please put the resources in the project root directory libs/dc-sdk, if you put it in other directory, the framework will not run properly.
+```
+
+## Configuration
+
+> The configuration is mainly used in the `NPM / YARN` way
+
+Since the `DC` framework sets `CESIUM_BASE_URL` to `./libs/dc-sdk/resources/` , you need to copy `Cesium` related static resources files: `Assets` , `Workers` , `ThirdParty `to `libs/dc-sdk/resources` directory of the project to ensure that the 3D scene can be rendered properly. You can also use `DC.baseUrl` to set the static resource base related to `Cesium` .
+
+`Webpack`
+
+[Project Template](https://github.com/cavencj/dc-vue-app)
+
+```js
+// webpack.config.js
+const path = require('path')
+const CopywebpackPlugin = require('copy-webpack-plugin')
+const dvgisDist = './node_modules/@dvgis'
+
+module.exports = {
+ plugins: [
+ new CopyWebpackPlugin([
+ {
+ from: path.join(dvgisDist, 'dc-sdk/dist/resources'),
+ to: 'libs/dc-sdk/resources',
+ },
+ ]),
+ ],
+}
+```
+
+`Vue2.x`
+
+[Project Template](https://github.com/dvgis/dc-vue)
+
+```js
+// vue.config.js
+const path = require('path')
+const CopywebpackPlugin = require('copy-webpack-plugin')
+const dvgisDist = './node_modules/@dvgis'
+module.exports = {
+ chainWebpack: (config) => {
+ config.plugin('copy').use(CopywebpackPlugin, [
+ [
+ {
+ from: path.join(dvgisDist, 'dc-sdk/dist/resources'),
+ to: 'libs/dc-sdk/resources',
+ },
+ ],
+ ])
+ },
+}
+```
+
+`Vue3.x`
+
+[Project Template](https://github.com/dvgis/dc-vue-next)
+
+```js
+// vue.config.js
+const path = require('path')
+const CopywebpackPlugin = require('copy-webpack-plugin')
+const dvgisDist = './node_modules/@dvgis'
+module.exports = {
+ chainWebpack: (config) => {
+ config.plugin('copy').use(CopywebpackPlugin, [
+ {
+ patterns: [
+ {
+ from: path.join(dvgisDist, 'dc-sdk/dist/resources'),
+ to: path.join(__dirname, 'dist', 'libs/dc-sdk/resources'),
+ },
+ ],
+ },
+ ])
+ },
+}
+```
+
+
+## Start
+
+```js
+global.DC = DC
+DC.use(DcCore) // node
+DC.ready(() => {
+ let viewer = new DC.Viewer(divId) // divId is the Id attribute value of a div node. If it is not passed in, the 3D scene cannot be initialized
+})
+```
+
+## Demo
+
+| ![picture](http://dc.dvgis.cn/examples/images/baselayer/baidu.png?v=3) | ![picture](http://dc.dvgis.cn/examples/images/baselayer/tdt.png?v=2) | ![picture](http://dc.dvgis.cn/examples/images/baselayer/arcgis.png?v=3) | ![picture](http://dc.dvgis.cn/examples/images/mini-scene/china.gif) |
+| :-----------------------------------------------------------: | :-----------------------------------------------------------: | :------------------------------------------------------------------: | :--------------------------------------------------------------: |
+| ![picture](http://dc.dvgis.cn/examples/images/mini-scene/dfmz.gif) | ![picture](http://dc.dvgis.cn/examples/images/mini-scene/factory.gif?v=1) | ![picture](http://dc.dvgis.cn/examples/images/layer/cluster_circle.gif) | ![picture](http://dc.dvgis.cn/examples/images/model/shp_custom_shader.gif) |
+| ![picture](http://dc.dvgis.cn/examples/images/overlay/polyline_image_trail.gif) | ![picture](http://dc.dvgis.cn/examples/images/overlay/wall_trail.gif?v=1) | ![picture](http://dc.dvgis.cn/examples/images/overlay/water.gif?v=4) | ![picture](http://dc.dvgis.cn/examples/images/overlay/plot-overlay.png?v=4) |
+
+[More>>](http://dc.dvgis.cn/#/examples)
+
+## Copyright
+
+```warning
+1. The framework is a basic platform, completely open source, which can be modified and reconstructed by any individual or institution without our authorization.
+2. We are not responsible for any problems arising from the modification of the framework by individuals and organizations.
+3. Some industrial plug-ins and tools will be added in the later stage, and the code will be open source appropriately.
+4. The package released by us may be used permanently and free of charge by any person or organization subject to:
+ 1) complete package reference;
+ 2) reserve this copyright information in the console output
+We reserve the right of final interpretation of this copyright information.
+```
+
+## Support
+
+> if dc-sdk can bring benefits to you, please support it ~
+
+
+
+
+
+
+
+## Thanks
diff --git a/examples/test.html b/examples/test.html
new file mode 100644
index 00000000..1e36ee2a
--- /dev/null
+++ b/examples/test.html
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+ demo
+
+
+
+
+
+
+
+
+
diff --git a/gulpfile.js b/gulpfile.js
new file mode 100644
index 00000000..0e4c2c58
--- /dev/null
+++ b/gulpfile.js
@@ -0,0 +1,346 @@
+/**
+ @author : Caven Chen
+ @date : 2023-05-06
+ **/
+
+'use strict'
+
+import fse from 'fs-extra'
+import path from 'path'
+import gulp from 'gulp'
+import esbuild from 'esbuild'
+import concat from 'gulp-concat'
+import { rollup } from 'rollup'
+import clean from 'gulp-clean'
+import commonjs from '@rollup/plugin-commonjs'
+import resolve from '@rollup/plugin-node-resolve'
+import scss from 'rollup-plugin-scss'
+import javascriptObfuscator from 'gulp-javascript-obfuscator'
+import { babel } from '@rollup/plugin-babel'
+import startServer from './server.js'
+import { uglify } from 'rollup-plugin-uglify'
+import inlineImage from 'esbuild-plugin-inline-image'
+import { glsl } from 'esbuild-plugin-glsl'
+
+const obfuscatorConfig = {
+ compact: true, //压缩代码
+ identifierNamesGenerator: 'hexadecimal', //标识符的混淆方式 hexadecimal(十六进制) mangled(短标识符)
+ renameGlobals: false, //是否启用全局变量和函数名称的混淆
+ rotateStringArray: true, //通过固定和随机(在代码混淆时生成)的位置移动数组。这使得将删除的字符串的顺序与其原始位置相匹配变得更加困难。如果原始源代码不小,建议使用此选项,因为辅助函数可以引起注意。
+ selfDefending: true, //混淆后的代码,不能使用代码美化,同时需要配置 compact:true;
+ stringArray: true, //删除字符串文字并将它们放在一个特殊的数组中
+ stringArrayEncoding: ['base64'],
+ stringArrayThreshold: 0.75,
+ transformObjectKeys: false,
+ unicodeEscapeSequence: false, //允许启用/禁用字符串转换为unicode转义序列。Unicode转义序列大大增加了代码大小,并且可以轻松地将字符串恢复为原始视图。建议仅对小型源代码启用此选项。
+}
+
+const buildConfig = {
+ entryPoints: ['src/DC.js'],
+ bundle: true,
+ color: true,
+ legalComments: `inline`,
+ logLimit: 0,
+ target: `es2020`,
+ minify: false,
+ sourcemap: false,
+ write: true,
+ logLevel: 'info',
+ external: [`http`, `https`, `url`, `zlib`],
+ plugins: [
+ inlineImage({
+ limit: -1,
+ }),
+ glsl(),
+ ],
+}
+
+const packageJson = fse.readJsonSync('./package.json')
+
+function getTime() {
+ let now = new Date()
+ let m = now.getMonth() + 1
+ m = m < 10 ? '0' + m : m
+ let d = now.getDate()
+ d = d < 10 ? '0' + d : d
+ return `${now.getFullYear()}-${m}-${d}`
+}
+
+async function buildNamespace(options) {
+ const bundle = await rollup({
+ input: 'libs/index.js',
+ plugins: [
+ commonjs(),
+ resolve({ preferBuiltins: true }),
+ babel({
+ babelHelpers: 'runtime',
+ presets: [
+ [
+ '@babel/preset-env',
+ {
+ modules: false,
+ targets: {
+ browsers: ['> 1%', 'last 2 versions', 'ie >= 10'],
+ },
+ },
+ ],
+ ],
+ plugins: ['@babel/plugin-transform-runtime'],
+ }),
+ uglify(),
+ ],
+ })
+ return bundle.write({
+ name: 'DC.__namespace',
+ file: options.node ? 'dist/namespace.cjs' : 'dist/namespace.js',
+ format: options.node ? 'cjs' : 'umd',
+ sourcemap: false,
+ })
+}
+
+async function buildCSS() {
+ const bundle = await rollup({
+ input: 'src/themes/index.js',
+ plugins: [
+ commonjs(),
+ resolve({ preferBuiltins: true }),
+ scss({
+ outputStyle: 'compressed',
+ fileName: 'dc.min.css',
+ }),
+ ],
+ })
+ return bundle.write({
+ file: 'dist/dc.min.css',
+ })
+}
+
+async function buildModules(options) {
+ const dcPath = path.join('src', 'DC.js')
+
+ const content = await fse.readFile(path.join('src', 'index.js'), 'utf8')
+
+ await fse.ensureFile(dcPath)
+
+ const exportVersion = `export const VERSION = '${packageJson.version}'`
+
+ const cmdOut_content = await fse.readFile(
+ path.join('src', 'copyright', 'cmdOut.js'),
+ 'utf8'
+ )
+
+ const exportCmdOut = `
+ export function __cmdOut() {
+ ${cmdOut_content
+ .replace('{{__VERSION__}}', packageJson.version)
+ .replace('{{__TIME__}}', getTime())
+ .replace(
+ '{{__ENGINE_VERSION__}}',
+ packageJson.devDependencies['@cesium/engine'].replace('^', '')
+ )
+ .replace('{{__AUTHOR__}}', packageJson.author)
+ .replace('{{__HOME_PAGE__}}', packageJson.homepage)
+ .replace('{{__REPOSITORY__}}', packageJson.repository)}
+ }`
+
+ const exportNamespace = `
+ export const __namespace = {
+ Cesium: exports.Cesium
+ }
+ `
+
+ // Build IIFE
+ if (options.iife) {
+ await fse.outputFile(
+ dcPath,
+ `
+ ${content}
+ ${exportVersion}
+ ${exportCmdOut}
+ `,
+ {
+ encoding: 'utf8',
+ }
+ )
+ await esbuild.build({
+ ...buildConfig,
+ format: 'iife',
+ globalName: 'DC',
+ outfile: path.join('dist', 'modules.js'),
+ })
+ }
+
+ // Build Node、
+ if (options.node) {
+ await fse.outputFile(
+ dcPath,
+ `
+ ${content}
+ ${exportNamespace}
+ ${exportVersion}
+ ${exportCmdOut}
+ `,
+ {
+ encoding: 'utf8',
+ }
+ )
+ await esbuild.build({
+ ...buildConfig,
+ format: 'cjs',
+ platform: 'node',
+ define: {
+ TransformStream: 'null',
+ },
+ outfile: path.join('dist', 'modules.cjs'),
+ })
+ }
+
+ // remove DC.js
+ await fse.remove(dcPath)
+}
+
+async function combineJs(options) {
+ // combine for iife
+ if (options.iife) {
+ if (options.obfuscate) {
+ await gulp
+ .src('dist/modules.js')
+ .pipe(javascriptObfuscator(obfuscatorConfig))
+ .pipe(gulp.src('dist/namespace.js'))
+ .pipe(concat('dc.min.js'))
+ .pipe(gulp.dest('dist'))
+ .on('end', () => {
+ addCopyright(options)
+ deleteTempFile(options)
+ })
+ } else {
+ await gulp
+ .src(['dist/modules.js', 'dist/namespace.js'])
+ .pipe(concat('dc.min.js'))
+ .pipe(gulp.dest('dist'))
+ .on('end', () => {
+ addCopyright(options)
+ deleteTempFile(options)
+ })
+ }
+ }
+
+ // combine for node
+ if (options.node) {
+ if (options.obfuscate) {
+ await gulp
+ .src('dist/modules.cjs')
+ .pipe(javascriptObfuscator(obfuscatorConfig))
+ .pipe(gulp.dest('dist'))
+ .on('end', async () => {
+ await gulp
+ .src(['dist/namespace.cjs', 'dist/modules.cjs'])
+ .pipe(concat('index.cjs'))
+ .pipe(gulp.dest('dist'))
+ .on('end', () => {
+ addCopyright(options)
+ deleteTempFile(options)
+ })
+ })
+ } else {
+ await gulp
+ .src(['dist/namespace.cjs', 'dist/modules.cjs'])
+ .pipe(concat('index.cjs'))
+ .pipe(gulp.dest('dist'))
+ .on('end', () => {
+ addCopyright(options)
+ deleteTempFile(options)
+ })
+ }
+ }
+}
+
+async function copyAssets() {
+ await fse.emptyDir('dist/resources')
+ await gulp
+ .src('./node_modules/@cesium/engine/Build/Workers/**', { nodir: true })
+ .pipe(gulp.dest('dist/resources/Workers'))
+ await gulp
+ .src('./node_modules/@cesium/engine/Source/Assets/**', { nodir: true })
+ .pipe(gulp.dest('dist/resources/Assets'))
+ await gulp
+ .src('./node_modules/@cesium/engine/Source/ThirdParty/**', { nodir: true })
+ .pipe(gulp.dest('dist/resources/ThirdParty'))
+}
+
+async function addCopyright(options) {
+ let header = await fse.readFile(
+ path.join('src', 'copyright', 'header.js'),
+ 'utf8'
+ )
+ header = header
+ .replace('{{__VERSION__}}', packageJson.version)
+ .replace('{{__AUTHOR__}}', packageJson.author)
+ .replace('{{__REPOSITORY__}}', packageJson.repository)
+
+ if (options.iife) {
+ let filePath = path.join('dist', 'dc.min.js')
+ const content = await fse.readFile(filePath, 'utf8')
+ await fse.outputFile(filePath, `${header}${content}`, { encoding: 'utf8' })
+ }
+
+ if (options.node) {
+ let filePath = path.join('dist', 'index.cjs')
+ const content = await fse.readFile(filePath, 'utf8')
+ await fse.outputFile(filePath, `${header}${content}`, { encoding: 'utf8' })
+ }
+}
+
+async function deleteTempFile(options) {
+ if (options.iife) {
+ await gulp
+ .src(['dist/namespace.js', 'dist/modules.js'], { read: false })
+ .pipe(clean())
+ }
+
+ if (options.node) {
+ await gulp
+ .src(['dist/namespace.cjs', 'dist/modules.cjs'], { read: false })
+ .pipe(clean())
+ }
+}
+
+export const build = gulp.series(
+ () => buildNamespace({ node: true }),
+ () => buildModules({ node: true }),
+ () => combineJs({ node: true }),
+ () => buildNamespace({ iife: true }),
+ () => buildModules({ iife: true }),
+ () => combineJs({ iife: true }),
+ buildCSS,
+ copyAssets
+)
+
+export const buildNode = gulp.series(
+ () => buildNamespace({ node: true }),
+ () => buildModules({ node: true }),
+ () => combineJs({ node: true }),
+ buildCSS,
+ copyAssets
+)
+
+export const buildIIFE = gulp.series(
+ () => buildNamespace({ iife: true }),
+ () => buildModules({ iife: true }),
+ () => combineJs({ iife: true }),
+ buildCSS,
+ copyAssets
+)
+
+export const buildRelease = gulp.series(
+ () => buildNamespace({ node: true }),
+ () => buildModules({ node: true }),
+ () => combineJs({ node: true, obfuscate: true }),
+ () => buildNamespace({ iife: true }),
+ () => buildModules({ iife: true }),
+ () => combineJs({ iife: true, obfuscate: true }),
+ buildCSS,
+ copyAssets
+)
+
+export const server = gulp.series(startServer)
diff --git a/libs/index.js b/libs/index.js
new file mode 100644
index 00000000..ccdbbc2b
--- /dev/null
+++ b/libs/index.js
@@ -0,0 +1,7 @@
+/**
+ @Author: Caven Chen
+ **/
+
+import * as Cesium from '@cesium/engine'
+
+export { Cesium }
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..c01c2c6d
--- /dev/null
+++ b/package.json
@@ -0,0 +1,76 @@
+{
+ "name": "@dvgis/dc-sdk",
+ "version": "3.0.3",
+ "license": "MIT",
+ "description": "The SDK is based on Cesium for secondary development of 2, 3D all-in-one WebGis application framework, the framework optimizes the use of Cesium and add some additional features, designed for developers to quickly build WebGis applications.",
+ "repository": "https://github.com/dvgis/dc-sdk.git",
+ "homepage": "https://www.dvgis.cn",
+ "keywords": [
+ "3D",
+ "webGL",
+ "map",
+ "globe",
+ "cesium",
+ "heatmap",
+ "turf",
+ "mapv",
+ "s3m"
+ ],
+ "author": "Caven Chen ",
+ "type": "module",
+ "main": "dist/index.cjs",
+ "scripts": {
+ "build": "rimraf dist && gulp build",
+ "build:node": "rimraf dist && gulp buildNode",
+ "build:iife": "rimraf dist && gulp buildIIFE",
+ "build:release": "rimraf dist && gulp buildRelease",
+ "prepublishOnly": "yarn run build:release",
+ "server": "gulp server"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.21.4",
+ "@babel/eslint-parser": "^7.21.8",
+ "@babel/plugin-transform-runtime": "^7.21.4",
+ "@babel/preset-env": "^7.21.5",
+ "@cesium/engine": "^2.3.0",
+ "@rollup/plugin-babel": "^6.0.3",
+ "@rollup/plugin-commonjs": "^24.1.0",
+ "@rollup/plugin-node-resolve": "^15.0.2",
+ "@rollup/plugin-terser": "^0.4.1",
+ "@turf/turf": "^6.5.0",
+ "chalk": "^5.2.0",
+ "esbuild": "^0.17.18",
+ "esbuild-plugin-glsl": "^1.2.1",
+ "esbuild-plugin-inline-image": "^0.0.9",
+ "eslint": "^8.40.0",
+ "eslint-config-prettier": "^8.8.0",
+ "eslint-plugin-import": "^2.27.5",
+ "eslint-plugin-node": "^11.1.0",
+ "eslint-plugin-prettier": "^4.2.1",
+ "eslint-plugin-promise": "^6.1.1",
+ "express": "^4.18.2",
+ "fs-extra": "^11.1.1",
+ "gulp": "^4.0.2",
+ "gulp-clean": "^0.4.0",
+ "gulp-concat": "^2.6.1",
+ "gulp-javascript-obfuscator": "^1.1.6",
+ "javascript-obfuscator": "^4.0.2",
+ "portfinder": "^1.0.32",
+ "postcss": "^8.4.23",
+ "prettier": "^2.8.8",
+ "rimraf": "^5.0.0",
+ "rollup": "^3.21.0",
+ "rollup-plugin-copy": "^3.4.0",
+ "rollup-plugin-obfuscator": "^1.0.3",
+ "rollup-plugin-postcss": "^4.0.2",
+ "rollup-plugin-require-context": "^1.0.1",
+ "rollup-plugin-scss": "^4.0.0",
+ "rollup-plugin-uglify": "^6.0.4",
+ "sass": "^1.62.1",
+ "shelljs": "^0.8.5",
+ "vinyl-sourcemaps-apply": "^0.2.1"
+ },
+ "files": [
+ "dist"
+ ]
+}
diff --git a/server.js b/server.js
new file mode 100644
index 00000000..5dde6cf1
--- /dev/null
+++ b/server.js
@@ -0,0 +1,30 @@
+/**
+ @author : Caven Chen
+ @date : 2023-05-08
+ */
+
+import express from 'express'
+import portfinder from 'portfinder'
+import fse from 'fs-extra'
+import shell from 'shelljs'
+import chalk from 'chalk'
+
+export default function start() {
+ let dist = 'dist'
+ const server = express()
+ portfinder.setBasePort(8081)
+ fse.exists(dist, (exists) => {
+ if (exists) {
+ portfinder.getPort((err, port) => {
+ server.listen(port)
+ shell.echo(
+ chalk.yellow(`the url is : http://localhost:${port}/test.html`)
+ )
+ server.use('/libs/dc-sdk/', express.static(dist))
+ server.use(express.static('examples'))
+ })
+ } else {
+ shell.echo(chalk.red(`please run build first`))
+ }
+ })
+}
diff --git a/src/copyright/cmdOut.js b/src/copyright/cmdOut.js
new file mode 100644
index 00000000..7e8b62a4
--- /dev/null
+++ b/src/copyright/cmdOut.js
@@ -0,0 +1,30 @@
+/**
+ @author : Caven Chen
+ @date : 2023-05-07
+ */
+
+// eslint-disable-next-line no-console
+console.clear()
+
+// eslint-disable-next-line no-console
+console.log(
+ `%c \n DC-SDK \n %c \n 用数字描绘世界之美 %c \n
+ 版本: {{__VERSION__}} - {{__TIME__}}
+ Cesium Engine 版本:{{__ENGINE_VERSION__}}
+ 开发作者: {{__AUTHOR__}}
+ 网站主页: {{__HOME_PAGE__}}
+ github: {{__REPOSITORY__}}
+ 授权信息:授权-永久使用此软件当前版本。 \n
+
+ 版权声明:
+ 1.框架作为一个基础平台,代码开源,任何个人和机构可以修改、重构,无需经过我方授权。
+ 2.任何个人和机构修改框架出现的问题,我方无需负责。
+ 3.后期会添加一些行业性的插件和工具,代码会适量开源。
+ 4.对于我方发布的框架包,任何个人和机构在遵守下列条件的前提下可以永久免费使用:
+ 1)程序包完整引用
+ 2)保留此版权信息在控制台输出
+ 我方保留对此版权信息的最终解释权。`,
+ 'font-size:20px;padding-left:70px;color:#EEB422',
+ 'font-size:14px;padding-left:50px;color:#EEB422;font-style:oblique',
+ 'font-size:12px;color:#0865ba'
+)
diff --git a/src/copyright/header.js b/src/copyright/header.js
new file mode 100644
index 00000000..40c2f6ea
--- /dev/null
+++ b/src/copyright/header.js
@@ -0,0 +1,25 @@
+/**
+ * @license
+ * @name dc-sdk
+ * @github {{__REPOSITORY__}}
+ * @author {{__AUTHOR__}}
+ * @organization dvgis
+ * @version {{__VERSION__}}
+ *
+ * Copyright (c) 2019-present Caven Chen
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Portions licensed separately.
+ * See https://github.com/dvgis/dc-sdk/blob/master/LICENSE.MD for full licensing details.
+ */
diff --git a/src/global-api/lib-utils.js b/src/global-api/lib-utils.js
new file mode 100644
index 00000000..ccc43876
--- /dev/null
+++ b/src/global-api/lib-utils.js
@@ -0,0 +1,24 @@
+/**
+ @author : Caven Chen
+ @date : 2023-05-06
+ */
+
+const cache = {}
+
+/**
+ * register lib
+ * @param name
+ * @param lib
+ */
+export function registerLib(name, lib) {
+ cache[name] = lib
+}
+
+/**
+ * get lib
+ * @param name
+ * @return {*}
+ */
+export function getLib(name) {
+ return cache[name]
+}
diff --git a/src/index.js b/src/index.js
new file mode 100644
index 00000000..394f4e60
--- /dev/null
+++ b/src/index.js
@@ -0,0 +1,79 @@
+/**
+ @Author: Caven Chen
+ **/
+import { getLib, registerLib } from './global-api/lib-utils.js'
+
+export { registerLib, getLib } from './global-api/lib-utils.js'
+
+let _baseUrl = './libs/dc-sdk/resources/'
+let _accessToken = ''
+
+export const config = {
+ set baseUrl(baseUrl) {
+ _baseUrl = baseUrl
+ },
+ get baseUrl() {
+ return _baseUrl
+ },
+ set accessToken(accessToken) {
+ _accessToken = accessToken
+ },
+ get accessToken() {
+ return _accessToken
+ },
+}
+
+export function ready(options = {}) {
+ if (options['baseUrl']) {
+ this.config.baseUrl = options['baseUrl']
+ }
+ if (options['accessToken']) {
+ this.config.accessToken = options['accessToken']
+ }
+
+ if (options['Cesium']) {
+ registerLib('Cesium', options['Cesium'])
+ } else {
+ registerLib('Cesium', this['__namespace']['Cesium'])
+ }
+
+ if (options['echarts']) {
+ registerLib('echarts', options['echarts'])
+ }
+
+ // if (options['turf']) {
+ // registerLib('turf', options['turf'])
+ // }
+
+ this['__cmdOut'] && this['__cmdOut']()
+
+ return new Promise((resolve, reject) => {
+ const Cesium = getLib('Cesium')
+ if (!Cesium) {
+ throw new Error('missing Cesium Lib')
+ }
+ this.config.baseUrl && Cesium.buildModuleUrl.setBaseUrl(this.config.baseUrl)
+ // register common modules
+ const modules = require('./modules')
+ Object.keys(modules).forEach((key) => {
+ this[key] = modules[key]
+ })
+
+ // register chart module
+ if (getLib('echarts')) {
+ const modules = require('./modules/chart')
+ Object.keys(modules).forEach((key) => {
+ this[key] = modules[key]
+ })
+ }
+
+ // register turf module
+ if (getLib('turf')) {
+ // todo
+ }
+
+ resolve()
+ }).catch((e) => {
+ throw new Error(e.message)
+ })
+}
diff --git a/src/modules/animation/Animation.js b/src/modules/animation/Animation.js
new file mode 100644
index 00000000..9fc96b9a
--- /dev/null
+++ b/src/modules/animation/Animation.js
@@ -0,0 +1,45 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-12-20 16:32:22
+ */
+
+class Animation {
+ constructor(viewer) {
+ this._viewer = viewer
+ this._options = {}
+ }
+
+ _bindEvent() {}
+
+ _unbindEvent() {}
+
+ /**
+ * Start globe rotate
+ * @returns {Animation}
+ */
+ start() {
+ this._viewer.clock.shouldAnimate = true
+ this._unbindEvent()
+ if (this._options.duration) {
+ let timer = setTimeout(() => {
+ this._unbindEvent()
+ this._options.callback &&
+ this._options.callback.call(this._options.context || this)
+ clearTimeout(timer)
+ }, Number(this._options.duration) * 1e3)
+ }
+ this._bindEvent()
+ return this
+ }
+
+ /**
+ * Stop globe rotate
+ * @returns {Animation}
+ */
+ stop() {
+ this._unbindEvent()
+ return this
+ }
+}
+
+export default Animation
diff --git a/src/modules/animation/AnimationType.js b/src/modules/animation/AnimationType.js
new file mode 100644
index 00000000..fd9cc0f8
--- /dev/null
+++ b/src/modules/animation/AnimationType.js
@@ -0,0 +1,8 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-08-01 00:12:06
+ */
+
+let AnimationType = {}
+
+export default AnimationType
diff --git a/src/modules/animation/index.js b/src/modules/animation/index.js
new file mode 100644
index 00000000..843d3730
--- /dev/null
+++ b/src/modules/animation/index.js
@@ -0,0 +1,17 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-03-05 22:15:27
+ */
+
+export { default as Animation } from './Animation'
+export { default as AnimationType } from './AnimationType'
+
+/**
+ * types
+ */
+export { default as AroundView } from './type/AroundView'
+export { default as AroundPoint } from './type/AroundPoint'
+export { default as CircleScan } from './type/CircleScan'
+export { default as Flying } from './type/Flying'
+export { default as GlobeRotate } from './type/GlobeRotate'
+export { default as RadarScan } from './type/RadarScan'
diff --git a/src/modules/animation/type/AroundPoint.js b/src/modules/animation/type/AroundPoint.js
new file mode 100644
index 00000000..86506f6a
--- /dev/null
+++ b/src/modules/animation/type/AroundPoint.js
@@ -0,0 +1,73 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-03-02 22:38:10
+ */
+import { Cesium } from '../../../namespace'
+import AnimationType from '../AnimationType'
+import Animation from '../Animation'
+import { Transform } from '../../transform'
+import Parse from '../../parse/Parse.js'
+
+class AroundPoint extends Animation {
+ constructor(viewer, position, options = {}) {
+ super(viewer)
+ this._position = Parse.parsePosition(position)
+ this._options = options
+ this._heading = viewer.camera.heading
+ this._aroundAmount = 0.2
+ }
+
+ get type() {
+ return AnimationType.AROUND_POINT
+ }
+
+ set position(position) {
+ this._position = Parse.parsePosition(position)
+ }
+
+ set aroundAmount(aroundAmount) {
+ this._aroundAmount = aroundAmount
+ }
+
+ /**
+ *
+ * @private
+ */
+ _bindEvent() {
+ this._viewer.clock.onTick.addEventListener(this._onAround, this)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _unbindEvent() {
+ this._viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)
+ this._viewer.clock.onTick.removeEventListener(this._onAround, this)
+ }
+
+ /**
+ *
+ * @param scene
+ * @param time
+ * @private
+ */
+ _onAround(scene, time) {
+ this._heading += Cesium.Math.toRadians(this._aroundAmount)
+ if (this._heading >= Math.PI * 2 || this._heading <= -Math.PI * 2) {
+ this._heading = 0
+ }
+ this._viewer.camera.lookAt(
+ Transform.transformWGS84ToCartesian(this._position),
+ new Cesium.HeadingPitchRange(
+ this._heading,
+ Cesium.Math.toRadians(this._options.pitch || 0),
+ this._options.range || 1000
+ )
+ )
+ }
+}
+
+AnimationType.AROUND_POINT = 'around_point'
+
+export default AroundPoint
diff --git a/src/modules/animation/type/AroundView.js b/src/modules/animation/type/AroundView.js
new file mode 100644
index 00000000..7c98fd63
--- /dev/null
+++ b/src/modules/animation/type/AroundView.js
@@ -0,0 +1,71 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-03-02 23:14:20
+ */
+
+import { Cesium } from '../../../namespace'
+import AnimationType from '../AnimationType'
+import Animation from '../Animation'
+
+class AroundView extends Animation {
+ constructor(viewer, options = {}) {
+ super(viewer)
+ this._options = options
+ this._heading = viewer.camera.heading
+ this._aroundAmount = 0.2
+ }
+
+ get type() {
+ return AnimationType.AROUND_VIEW
+ }
+
+ set aroundAmount(aroundAmount) {
+ this._aroundAmount = aroundAmount
+ return this
+ }
+
+ /**
+ *
+ * @private
+ */
+ _bindEvent() {
+ this._viewer.clock.onTick.addEventListener(this._onAround, this)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _unbindEvent() {
+ this._viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)
+ this._viewer.clock.onTick.removeEventListener(this._onAround, this)
+ }
+
+ /**
+ *
+ * @param scene
+ * @param time
+ * @private
+ */
+ _onAround(scene, time) {
+ this._heading += Cesium.Math.toRadians(this._aroundAmount)
+ if (this._heading >= Math.PI * 2 || this._heading <= -Math.PI * 2) {
+ this._heading = 0
+ }
+ this._viewer.camera.setView({
+ orientation: {
+ heading: this._heading,
+ pitch: this._options.pitch
+ ? Cesium.Math.toRadians(this._options.pitch)
+ : this._viewer.camera.pitch,
+ roll: this._options.roll
+ ? Cesium.Math.toRadians(this._options.roll)
+ : this._viewer.camera.roll,
+ },
+ })
+ }
+}
+
+AnimationType.AROUND_VIEW = 'around_view'
+
+export default AroundView
diff --git a/src/modules/animation/type/CircleScan.js b/src/modules/animation/type/CircleScan.js
new file mode 100644
index 00000000..c1573a83
--- /dev/null
+++ b/src/modules/animation/type/CircleScan.js
@@ -0,0 +1,87 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-12-01 20:26:02
+ */
+
+import { Cesium } from '../../../namespace'
+import AnimationType from '../AnimationType'
+import Animation from '../Animation'
+import { Util } from '../../utils'
+import { Transform } from '../../transform'
+import Parse from '../../parse/Parse.js'
+
+const CircleScanShader = require('@dc-modules/material/shader/circle/CircleScanShader.glsl')
+
+class CircleScan extends Animation {
+ constructor(viewer, position, radius, options = {}) {
+ super(viewer)
+ this._delegate = undefined
+ this._position = Parse.parsePosition(position)
+ this._radius = radius || 100
+ this._color = options.color || Cesium.Color.RED
+ this._speed = options.speed || 2
+ }
+
+ get type() {
+ return AnimationType.CIRCLE_SCAN
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountContent() {
+ let center = Transform.transformWGS84ToCartesian(this._position)
+ let up = Cesium.Ellipsoid.WGS84.geodeticSurfaceNormal(
+ center,
+ new Cesium.Cartesian3()
+ )
+ let self = this
+ this._delegate = new Cesium.PostProcessStage({
+ name: Util.uuid(),
+ fragmentShader: CircleScanShader,
+ uniforms: {
+ centerWC: function () {
+ return center
+ },
+ normalWC: function () {
+ return up
+ },
+ radius: function () {
+ return self._radius
+ },
+ speed: function () {
+ return self._speed
+ },
+ color: function () {
+ return self._color
+ },
+ },
+ })
+ }
+
+ /**
+ *
+ * @returns {CircleScan}
+ */
+ start() {
+ !this._delegate && this._mountContent()
+ this._delegate && this._viewer.scene.postProcessStages.add(this._delegate)
+ return this
+ }
+
+ /**
+ *
+ * @returns {CircleScan}
+ */
+ stop() {
+ this._delegate &&
+ this._viewer.scene.postProcessStages.remove(this._delegate)
+ this._delegate = undefined
+ return this
+ }
+}
+
+AnimationType.CIRCLE_SCAN = 'circle_scan'
+
+export default CircleScan
diff --git a/src/modules/animation/type/Flying.js b/src/modules/animation/type/Flying.js
new file mode 100644
index 00000000..9889602f
--- /dev/null
+++ b/src/modules/animation/type/Flying.js
@@ -0,0 +1,122 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-01-09 20:21:33
+ */
+
+import { Cesium } from '../../../namespace'
+import AnimationType from '../AnimationType'
+import Animation from '../Animation'
+import { Transform } from '../../transform'
+import Parse from '../../parse/Parse.js'
+
+class Flying extends Animation {
+ constructor(viewer, options = {}) {
+ super(viewer)
+ this._options = options
+ this._positions = []
+ this._durations = [3]
+ this._currentIndex = 0
+ this._timer = undefined
+ }
+
+ get type() {
+ return AnimationType.FLYING
+ }
+
+ set positions(positions) {
+ this._positions = Parse.parsePositions(positions)
+ return this
+ }
+
+ get positions() {
+ return this._positions
+ }
+
+ set durations(durations) {
+ this._durations = durations
+ return this
+ }
+
+ get durations() {
+ return this._durations
+ }
+
+ /**
+ *
+ * @private
+ */
+ _cameraFly() {
+ let self = this
+ let camera = this._viewer.camera
+ let position = this._positions[this._currentIndex]
+ let callback = () => {
+ let nextPosition = self._positions[self._currentIndex + 1]
+ if (nextPosition) {
+ self._currentIndex++
+ if (self._currentIndex <= self._positions.length - 1) {
+ self._timer = setTimeout(() => {
+ self._cameraFly()
+ }, (self._options.dwellTime || 1) * 1e3)
+ }
+ } else if (!nextPosition && self._options.loop) {
+ self._currentIndex = 0
+ self._timer = setTimeout(() => {
+ self._cameraFly()
+ }, (self._options.dwellTime || 1) * 1e3)
+ }
+ self._options.callback && self._options.callback(self._currentIndex)
+ }
+ if (position) {
+ camera.flyTo({
+ destination: Transform.transformWGS84ToCartesian(position),
+ orientation: {
+ heading: Cesium.Math.toRadians(position.heading),
+ pitch: Cesium.Math.toRadians(position.pitch),
+ roll: Cesium.Math.toRadians(position.roll),
+ },
+ complete: callback,
+ duration:
+ this._durations.length === 1
+ ? this._durations[0]
+ : this._durations[this._currentIndex],
+ })
+ }
+ }
+
+ /**
+ *
+ * @returns {Flying}
+ */
+ start() {
+ if (this._positions && this._positions.length) {
+ this._currentIndex = 0
+ this._cameraFly()
+ }
+ return this
+ }
+
+ /**
+ *
+ * @returns {Flying}
+ */
+ pause() {
+ this._viewer.camera.cancelFlight()
+ this._timer && clearTimeout(this._timer)
+ return this
+ }
+
+ /**
+ *
+ * @returns {Flying}
+ */
+ restore() {
+ if (this._positions && this._positions.length) {
+ this._cameraFly()
+ }
+ return this
+ }
+}
+
+AnimationType.FLYING = 'flying'
+
+export default Flying
diff --git a/src/modules/animation/type/GlobeRotate.js b/src/modules/animation/type/GlobeRotate.js
new file mode 100644
index 00000000..21787689
--- /dev/null
+++ b/src/modules/animation/type/GlobeRotate.js
@@ -0,0 +1,62 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-30 20:47:25
+ */
+
+import { Cesium } from '../../../namespace'
+import AnimationType from '../AnimationType'
+import Animation from '../Animation'
+
+class GlobeRotate extends Animation {
+ constructor(viewer, options = {}) {
+ super(viewer)
+ this._options = options
+ }
+
+ get type() {
+ return AnimationType.GLOBE_ROTATE
+ }
+
+ /**
+ * @param scene
+ * @param time
+ * @returns {boolean}
+ * @private
+ */
+ _icrf(scene, time) {
+ if (scene.mode !== Cesium.SceneMode.SCENE3D) {
+ return true
+ }
+ let icrfToFixed = Cesium.Transforms.computeIcrfToFixedMatrix(time)
+ if (icrfToFixed) {
+ let camera = this._viewer.camera
+ let offset = Cesium.Cartesian3.clone(camera.position)
+ let transform = Cesium.Matrix4.fromRotationTranslation(icrfToFixed)
+ camera.lookAtTransform(transform, offset)
+ }
+ }
+
+ /**
+ * Bind the Event
+ * @private
+ */
+ _bindEvent() {
+ this._viewer.clock.multiplier = this._options.speed || 12 * 1000
+ this._viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)
+ this._viewer.scene.postUpdate.addEventListener(this._icrf, this)
+ }
+
+ /**
+ * Unbind the Event
+ * @private
+ */
+ _unbindEvent() {
+ this._viewer.clock.multiplier = 1
+ this._viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)
+ this._viewer.scene.postUpdate.removeEventListener(this._icrf, this)
+ }
+}
+
+AnimationType.GLOBE_ROTATE = 'globe_rotate'
+
+export default GlobeRotate
diff --git a/src/modules/animation/type/RadarScan.js b/src/modules/animation/type/RadarScan.js
new file mode 100644
index 00000000..a5979342
--- /dev/null
+++ b/src/modules/animation/type/RadarScan.js
@@ -0,0 +1,100 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-12-01 20:40:02
+ */
+
+import { Cesium } from '../../../namespace'
+import AnimationType from '../AnimationType'
+import Animation from '../Animation'
+import { Util } from '../../utils'
+import { Transform } from '../../transform'
+import Parse from '../../parse/Parse.js'
+import RadarScanShader from '../../material/shader/radar/RadarScanShader.glsl'
+
+class RadarScan extends Animation {
+ constructor(viewer, position, radius, options = {}) {
+ super(viewer)
+ this._position = Parse.parsePosition(position)
+ this._radius = radius || 100
+ this._color = options.color || Cesium.Color.BLUE
+ this._speed = options.speed || 3
+ this._delegate = undefined
+ }
+
+ get type() {
+ return AnimationType.RADAR_SCAN
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountContent() {
+ let center = Transform.transformWGS84ToCartesian(this._position)
+ let up = Cesium.Ellipsoid.WGS84.geodeticSurfaceNormal(
+ center,
+ new Cesium.Cartesian3()
+ )
+ let time = new Date().getTime()
+ let self = this
+ this._delegate = new Cesium.PostProcessStage({
+ name: Util.uuid(),
+ fragmentShader: RadarScanShader,
+ uniforms: {
+ centerWC: function () {
+ return center
+ },
+ planeNormalWC: function () {
+ return up
+ },
+ lineNormalWC: function () {
+ let rotateQ = new Cesium.Quaternion()
+ let rotateM = new Cesium.Matrix3()
+ let east = Cesium.Cartesian3.cross(
+ Cesium.Cartesian3.UNIT_Z,
+ up,
+ new Cesium.Cartesian3()
+ )
+ let now = new Date().getTime()
+ let angle = Cesium.Math.PI * 2 * ((now - time) / 1e4) * self._speed
+ Cesium.Quaternion.fromAxisAngle(up, angle, rotateQ)
+ Cesium.Matrix3.fromQuaternion(rotateQ, rotateM)
+ Cesium.Matrix3.multiplyByVector(rotateM, east, east)
+ Cesium.Cartesian3.normalize(east, east)
+ return east
+ },
+ radius: function () {
+ return self._radius
+ },
+ color: function () {
+ return self._color
+ },
+ },
+ })
+ }
+
+ /**
+ *
+ * @returns {RadarScan}
+ */
+ start() {
+ !this._delegate && this._mountContent()
+ this._delegate && this._viewer.scene.postProcessStages.add(this._delegate)
+ return this
+ }
+
+ /**
+ *
+ * @returns {RadarScan}
+ */
+ stop() {
+ this._delegate &&
+ this._viewer.scene.postProcessStages.remove(this._delegate)
+ this._delegate = undefined
+ return this
+ }
+}
+
+AnimationType.RADAR_SCAN = 'radar_scan'
+
+export default RadarScan
diff --git a/src/modules/chart/ChartLayer.js b/src/modules/chart/ChartLayer.js
new file mode 100644
index 00000000..3fb07949
--- /dev/null
+++ b/src/modules/chart/ChartLayer.js
@@ -0,0 +1,76 @@
+/**
+ @author : Caven Chen
+ @date : 2023-05-23
+ */
+
+import { echarts } from '../../namespace'
+import { Layer } from '../layer'
+const { init } = echarts
+
+class ChartLayer extends Layer {
+ constructor(id, viewer) {
+ super(id)
+ this._viewer.canvas.setAttribute('tabIndex', '0')
+ this._chartEl = this._createChartElement()
+ this._chart = init(this._chartEl)
+ this._show = true
+ Object(this._chart.getZr()).viewer = viewer
+ }
+
+ get id() {
+ return this._id
+ }
+
+ set show(show) {
+ this._show = show
+ this._chartEl.style.visibility = show ? 'visible' : 'hidden'
+ }
+
+ get show() {
+ return this._show
+ }
+
+ /**
+ *
+ * @returns {HTMLDivElement}
+ * @private
+ */
+ _createChartElement() {
+ let canvas = this._viewer.scene.canvas
+ let el = document.createElement('div')
+ el.setAttribute('id', this._id)
+ el.style.cssText = `position:absolute; top:0; left:0; width: ${canvas.clientWidth}px; height: ${canvas.clientHeight}px;pointer-events:none;`
+ this._viewer.container.appendChild(el)
+ return el
+ }
+
+ /**
+ *
+ * @param option
+ * @returns {ChartLayer}
+ */
+ setOption(option = {}) {
+ this._chart.setOption({ ...option, GLMap: {}, animation: false })
+ return this
+ }
+
+ /**
+ *
+ * @returns {ChartLayer}
+ */
+ clear() {
+ this._chart.clear()
+ return this
+ }
+
+ /**
+ *
+ * @returns {ChartLayer}
+ */
+ resize() {
+ this._chart.resize()
+ return this
+ }
+}
+
+export default ChartLayer
diff --git a/src/modules/chart/GLMapCoordSys.js b/src/modules/chart/GLMapCoordSys.js
new file mode 100644
index 00000000..c3067ea1
--- /dev/null
+++ b/src/modules/chart/GLMapCoordSys.js
@@ -0,0 +1,84 @@
+/**
+ @author : Caven Chen
+ @date : 2023-05-23
+ */
+
+import { echarts, Cesium } from '../../namespace'
+const { Cartesian3, Ellipsoid, Math: CesiumMath } = Cesium
+const { graphic, matrix } = echarts
+
+class GLMapCoordSys {
+ constructor(api) {
+ this._api = api
+ this._viewer = Object(api.getZr()).viewer
+ this._mapOffset = [0, 0]
+ this.dimensions = ['lng', 'lat']
+ }
+
+ getViewer() {
+ return this._viewer
+ }
+
+ setMapOffset(mapOffset) {
+ this._mapOffset = mapOffset
+ return this
+ }
+
+ dataToPoint(data) {
+ let result = []
+ let cartesian3 = Cartesian3.fromDegrees(data[0], data[1])
+ if (!cartesian3) {
+ return result
+ }
+ let up = Ellipsoid.WGS84.geodeticSurfaceNormal(cartesian3, new Cartesian3())
+ let cd = this._viewer.camera.direction
+ if (Cartesian3.dot(up, cd) >= 0) {
+ return result
+ }
+ let coords = this._viewer.scene.cartesianToCanvasCoordinates(cartesian3)
+ if (!coords) {
+ return result
+ }
+ return [coords.x - this._mapOffset[0], coords.y - this._mapOffset[1]]
+ }
+
+ pointToData(point) {
+ let ellipsoid = this._viewer.scene.globe.ellipsoid
+ let cartesian3 = new Cartesian3(
+ point[0] + this._mapOffset[0],
+ point[1] + this._mapOffset[1],
+ 0
+ )
+ let cartographic = ellipsoid.cartesianToCartographic(cartesian3)
+ return [
+ CesiumMath.toDegrees(cartographic.longitude),
+ CesiumMath.toDegrees(cartographic.latitude),
+ ]
+ }
+
+ getViewRect() {
+ let api = this._api
+ return new graphic.BoundingRect(0, 0, api.getWidth(), api.getHeight())
+ }
+
+ getRoamTransform() {
+ return matrix.create()
+ }
+
+ static create(ecModel, api) {
+ let coordinateSys = undefined
+ ecModel.eachComponent('GLMap', function (model) {
+ coordinateSys = new GLMapCoordSys(api)
+ coordinateSys.setMapOffset(model['__mapOffset'] || [0, 0])
+ model.coordinateSystem = coordinateSys
+ })
+ ecModel.eachSeries(function (model) {
+ 'GLMap' === model.get('coordinateSystem') &&
+ (model.coordinateSystem = coordinateSys)
+ })
+ }
+}
+
+GLMapCoordSys.dimensions = ['lng', 'lat']
+
+export default GLMapCoordSys
diff --git a/src/modules/chart/GLMapModel.js b/src/modules/chart/GLMapModel.js
new file mode 100644
index 00000000..1eb0434b
--- /dev/null
+++ b/src/modules/chart/GLMapModel.js
@@ -0,0 +1,18 @@
+/**
+ @author : Caven Chen
+ @date : 2023-05-23
+ */
+
+import { echarts } from '../../namespace'
+
+const { extendComponentModel } = echarts
+
+extendComponentModel({
+ type: 'GLMap',
+ getViewer() {
+ return Object(this.getZr()).viewer
+ },
+ defaultOption: {
+ roam: false,
+ },
+})
diff --git a/src/modules/chart/GLMapView.js b/src/modules/chart/GLMapView.js
new file mode 100644
index 00000000..b41d9166
--- /dev/null
+++ b/src/modules/chart/GLMapView.js
@@ -0,0 +1,26 @@
+/**
+ @Author: Caven Chen
+**/
+
+import { echarts } from '../../namespace'
+
+const { extendComponentView } = echarts
+
+extendComponentView({
+ type: 'GLMap',
+ init: function (ecModel, api) {
+ this.api = api
+ let viewer = api.getZr().viewer
+ viewer.scene.postRender.addEventListener(this.moveHandler, this)
+ },
+ moveHandler: function (t, e) {
+ this.api.dispatchAction({
+ type: 'GLMapRoam',
+ })
+ },
+ render: function (t, e, i) {},
+ dispose: function (t) {
+ let viewer = this.api.getZr().viewer
+ viewer.scene.postRender.removeEventListener(this.moveHandler, this)
+ },
+})
diff --git a/src/modules/chart/index.js b/src/modules/chart/index.js
new file mode 100644
index 00000000..0cd1a111
--- /dev/null
+++ b/src/modules/chart/index.js
@@ -0,0 +1,24 @@
+/**
+ @Author: Caven Chen
+**/
+
+import { echarts } from '../../namespace'
+
+import './GLMapModel'
+import './GLMapView'
+import GLMapCoordSys from './GLMapCoordSys'
+import ChartLayer from './ChartLayer.js'
+
+const { registerAction, registerCoordinateSystem } = echarts
+
+registerCoordinateSystem('GLMap', GLMapCoordSys)
+registerAction(
+ {
+ type: 'GLMapRoam',
+ event: 'GLMapRoam',
+ update: 'updateLayout',
+ },
+ function (payload, ecModel) {}
+)
+
+export { ChartLayer }
diff --git a/src/modules/effect/Effect.js b/src/modules/effect/Effect.js
new file mode 100644
index 00000000..d4e1bfdd
--- /dev/null
+++ b/src/modules/effect/Effect.js
@@ -0,0 +1,70 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-14 23:49:14
+ */
+
+import BlackAndWhite from './type/BlackAndWhite'
+import Bloom from './type/Bloom'
+import Brightness from './type/Brightness'
+import DepthOfField from './type/DepthOfField'
+import LensFlare from './type/LensFlare'
+import NightVision from './type/NightVision'
+import Silhouette from './type/Silhouette'
+
+class Effect {
+ constructor() {
+ this._comps = {
+ blackAndWhite: new BlackAndWhite(),
+ bloom: new Bloom(),
+ brightness: new Brightness(),
+ depthOfField: new DepthOfField(),
+ lensFlare: new LensFlare(),
+ night: new NightVision(),
+ silhouette: new Silhouette(),
+ }
+ }
+
+ get blackAndWhite() {
+ return this._comps.blackAndWhite
+ }
+
+ get bloom() {
+ return this._comps.bloom
+ }
+
+ get brightness() {
+ return this._comps.brightness
+ }
+
+ get depthOfField() {
+ return this._comps.depthOfField
+ }
+
+ get lensFlare() {
+ return this._comps.lensFlare
+ }
+
+ get night() {
+ return this._comps.night
+ }
+
+ get silhouette() {
+ return this._comps.silhouette
+ }
+
+ /**
+ *
+ * @param viewer
+ */
+ install(viewer) {
+ Object.keys(this._comps).forEach((key) => {
+ this._comps[key].addTo(viewer)
+ })
+ Object.defineProperty(viewer, 'effect', {
+ value: this,
+ writable: false,
+ })
+ }
+}
+
+export default Effect
diff --git a/src/modules/effect/type/BlackAndWhite.js b/src/modules/effect/type/BlackAndWhite.js
new file mode 100644
index 00000000..edb00ffc
--- /dev/null
+++ b/src/modules/effect/type/BlackAndWhite.js
@@ -0,0 +1,80 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-14 23:51:47
+ */
+
+import { Cesium } from '../../../namespace'
+import State from '../../state/State'
+
+class BlackAndWhite {
+ constructor() {
+ this._viewer = undefined
+ this._delegate = undefined
+ this._enable = false
+ this._gradations = 1
+ this._selected = []
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return 'black_and_white'
+ }
+
+ set enable(enable) {
+ this._enable = enable
+ if (enable && this._viewer && !this._delegate) {
+ this._createPostProcessStage()
+ }
+ this._delegate && (this._delegate.enabled = enable)
+ }
+
+ get enable() {
+ return this._enable
+ }
+
+ set gradations(gradations) {
+ this._gradations = gradations
+ this._delegate && (this._delegate.uniforms.gradations = gradations)
+ }
+
+ get gradations() {
+ return this._gradations
+ }
+
+ set selected(selected) {
+ this._selected = selected
+ this._delegate && (this._delegate.selected = selected)
+ }
+
+ get selected() {
+ return this._selected
+ }
+
+ /**
+ *
+ * @private
+ */
+ _createPostProcessStage() {
+ this._delegate = Cesium.PostProcessStageLibrary.createBlackAndWhiteStage()
+ if (this._delegate) {
+ this._delegate.uniforms.gradations = this._gradations
+ this._viewer.scene.postProcessStages.add(this._delegate)
+ }
+ }
+
+ /**
+ *
+ * @param viewer
+ * @returns {BlackAndWhite}
+ */
+ addTo(viewer) {
+ if (!viewer) {
+ return this
+ }
+ this._viewer = viewer
+ this._state = State.ADDED
+ return this
+ }
+}
+
+export default BlackAndWhite
diff --git a/src/modules/effect/type/Bloom.js b/src/modules/effect/type/Bloom.js
new file mode 100644
index 00000000..78acfb23
--- /dev/null
+++ b/src/modules/effect/type/Bloom.js
@@ -0,0 +1,130 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-14 23:50:27
+ */
+
+import State from '../../state/State'
+
+class Bloom {
+ constructor() {
+ this._viewer = undefined
+ this._enable = false
+ this._contrast = 128
+ this._brightness = -0.3
+ this._glowOnly = false
+ this._delta = 1
+ this._sigma = 3.8
+ this._stepSize = 5
+ this._selected = []
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return 'bloom'
+ }
+
+ set enable(enable) {
+ this._enable = enable
+ if (enable && this._viewer && !this._delegate) {
+ this._createPostProcessStage()
+ }
+ this._delegate && (this._delegate.enabled = enable)
+ }
+
+ get enable() {
+ return this._enable
+ }
+
+ set contrast(contrast) {
+ this._contrast = contrast
+ this._delegate && (this._delegate.uniforms.contrast = contrast)
+ }
+
+ get contrast() {
+ return this._contrast
+ }
+
+ set brightness(brightness) {
+ this._brightness = brightness
+ this._delegate && (this._delegate.uniforms.brightness = brightness)
+ }
+
+ get brightness() {
+ return this._brightness
+ }
+
+ set glowOnly(glowOnly) {
+ this._glowOnly = glowOnly
+ this._delegate && (this._delegate.uniforms.glowOnly = glowOnly)
+ }
+
+ get glowOnly() {
+ return this._glowOnly
+ }
+
+ set delta(delta) {
+ this._delta = delta
+ this._delegate && (this._delegate.uniforms.delta = delta)
+ }
+
+ get delta() {
+ return this._delta
+ }
+
+ set sigma(sigma) {
+ this._sigma = sigma
+ this._delegate && (this._delegate.uniforms.sigma = sigma)
+ }
+
+ get sigma() {
+ return this._sigma
+ }
+
+ set stepSize(stepSize) {
+ this._stepSize = stepSize
+ this._delegate && (this._delegate.uniforms.stepSize = stepSize)
+ }
+
+ get stepSize() {
+ return this._stepSize
+ }
+
+ set selected(selected) {
+ this._selected = selected
+ this._delegate && (this._delegate.selected = selected)
+ }
+
+ get selected() {
+ return this._selected
+ }
+
+ /**
+ *
+ * @private
+ */
+ _createPostProcessStage() {
+ this._delegate = this._viewer.scene.postProcessStages.bloom
+ this._delegate.uniforms.contrast = this._contrast
+ this._delegate.uniforms.brightness = this._brightness
+ this._delegate.uniforms.glowOnly = this._glowOnly
+ this._delegate.uniforms.delta = this._delta
+ this._delegate.uniforms.sigma = this._sigma
+ this._delegate.uniforms.stepSize = this._stepSize
+ }
+
+ /**
+ *
+ * @param viewer
+ * @returns {Bloom}
+ */
+ addTo(viewer) {
+ if (!viewer) {
+ return this
+ }
+ this._viewer = viewer
+ this._state = State.ADDED
+ return this
+ }
+}
+
+export default Bloom
diff --git a/src/modules/effect/type/Brightness.js b/src/modules/effect/type/Brightness.js
new file mode 100644
index 00000000..acc5b46f
--- /dev/null
+++ b/src/modules/effect/type/Brightness.js
@@ -0,0 +1,80 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-14 23:51:47
+ */
+
+import { Cesium } from '../../../namespace'
+import State from '../../state/State'
+
+class Brightness {
+ constructor() {
+ this._viewer = undefined
+ this._delegate = undefined
+ this._enable = false
+ this._intensity = 1
+ this._selected = []
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return 'brightness'
+ }
+
+ set enable(enable) {
+ this._enable = enable
+ if (enable && this._viewer && !this._delegate) {
+ this._createPostProcessStage()
+ }
+ this._delegate && (this._delegate.enabled = enable)
+ }
+
+ get enable() {
+ return this._enable
+ }
+
+ set intensity(intensity) {
+ this._intensity = intensity
+ this._delegate && (this._delegate.uniforms.brightness = intensity)
+ }
+
+ get intensity() {
+ return this._intensity
+ }
+
+ set selected(selected) {
+ this._selected = selected
+ this._delegate && (this._delegate.selected = selected)
+ }
+
+ get selected() {
+ return this._selected
+ }
+
+ /**
+ *
+ * @private
+ */
+ _createPostProcessStage() {
+ this._delegate = Cesium.PostProcessStageLibrary.createBrightnessStage()
+ if (this._delegate) {
+ this._delegate.uniforms.brightness = this._intensity
+ this._viewer.scene.postProcessStages.add(this._delegate)
+ }
+ }
+
+ /**
+ *
+ * @param viewer
+ * @returns {Brightness}
+ */
+ addTo(viewer) {
+ if (!viewer) {
+ return this
+ }
+ this._viewer = viewer
+ this._state = State.ADDED
+ return this
+ }
+}
+
+export default Brightness
diff --git a/src/modules/effect/type/DepthOfField.js b/src/modules/effect/type/DepthOfField.js
new file mode 100644
index 00000000..10558af6
--- /dev/null
+++ b/src/modules/effect/type/DepthOfField.js
@@ -0,0 +1,126 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-14 23:51:47
+ */
+
+import { Cesium } from '../../../namespace'
+import State from '../../state/State'
+
+class DepthOfField {
+ constructor() {
+ this._viewer = undefined
+ this._delegate = undefined
+ this._enable = false
+ this._focalDistance = 87
+ this._delta = 1
+ this._sigma = 3.8
+ this._stepSize = 2.5
+ this._selected = []
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return 'depth_of_field'
+ }
+
+ set enable(enable) {
+ this._enable = enable
+ if (
+ enable &&
+ this._viewer &&
+ Cesium.PostProcessStageLibrary.isDepthOfFieldSupported(
+ this._viewer.scene
+ ) &&
+ !this._delegate
+ ) {
+ this._createPostProcessStage()
+ }
+ this._delegate && (this._delegate.enabled = enable)
+ return this
+ }
+
+ get enable() {
+ return this._enable
+ }
+
+ set focalDistance(focalDistance) {
+ this._focalDistance = focalDistance
+ this._delegate && (this._delegate.uniforms.focalDistance = focalDistance)
+ return this
+ }
+
+ get focalDistance() {
+ return this._focalDistance
+ }
+
+ set delta(delta) {
+ this._delta = delta
+ this._delegate && (this._delegate.uniforms.delta = delta)
+ return this
+ }
+
+ get delta() {
+ return this._delta
+ }
+
+ set sigma(sigma) {
+ this._sigma = sigma
+ this._delegate && (this._delegate.uniforms.sigma = sigma)
+ return this
+ }
+
+ get sigma() {
+ return this._sigma
+ }
+
+ set stepSize(stepSize) {
+ this._stepSize = stepSize
+ this._delegate && (this._delegate.uniforms.stepSize = stepSize)
+ return this
+ }
+
+ get stepSize() {
+ return this._stepSize
+ }
+
+ set selected(selected) {
+ this._selected = selected
+ this._delegate && (this._delegate.selected = selected)
+ return this
+ }
+
+ get selected() {
+ return this._selected
+ }
+
+ /**
+ *
+ * @private
+ */
+ _createPostProcessStage() {
+ this._delegate = Cesium.PostProcessStageLibrary.createDepthOfFieldStage()
+ if (this._delegate) {
+ this._delegate.uniforms.focalDistance = this._focalDistance
+ this._delegate.uniforms.delta = this._delta
+ this._delegate.uniforms.sigma = this._sigma
+ this._delegate.uniforms.stepSize = this._stepSize
+ this._viewer.scene.postProcessStages.add(this._delegate)
+ }
+ }
+
+ /**
+ *
+ * @param viewer
+ * @returns {DepthOfField}
+ */
+ addTo(viewer) {
+ if (!viewer) {
+ return this
+ }
+ this._viewer = viewer
+ this._state = State.ADDED
+ return this
+ }
+}
+
+export default DepthOfField
diff --git a/src/modules/effect/type/LensFlare.js b/src/modules/effect/type/LensFlare.js
new file mode 100644
index 00000000..c97954dd
--- /dev/null
+++ b/src/modules/effect/type/LensFlare.js
@@ -0,0 +1,119 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-14 23:51:47
+ */
+
+import { Cesium } from '../../../namespace'
+import State from '../../state/State'
+
+class LensFlare {
+ constructor() {
+ this._viewer = undefined
+ this._delegate = undefined
+ this._enable = false
+ this._intensity = 6
+ this._distortion = 61
+ this._dirtAmount = 0.4
+ this._haloWidth = 0.4
+ this._selected = []
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return 'lens_flare'
+ }
+
+ set enable(enable) {
+ this._enable = enable
+ if (enable && this._viewer && !this._delegate) {
+ this._createPostProcessStage()
+ }
+ this._delegate && (this._delegate.enabled = enable)
+ return this
+ }
+
+ get enable() {
+ return this._enable
+ }
+
+ set intensity(intensity) {
+ this._intensity = intensity
+ this._delegate && (this._delegate.uniforms.intensity = intensity)
+ return this
+ }
+
+ get intensity() {
+ return this._intensity
+ }
+
+ set distortion(distortion) {
+ this._distortion = distortion
+ this._delegate && (this._delegate.uniforms.distortion = distortion)
+ return this
+ }
+
+ get distortion() {
+ return this._distortion
+ }
+
+ set dirtAmount(dirtAmount) {
+ this._dirtAmount = dirtAmount
+ this._delegate && (this._delegate.uniforms.dirtAmount = dirtAmount)
+ return this
+ }
+
+ get dirtAmount() {
+ return this._dirtAmount
+ }
+
+ set haloWidth(haloWidth) {
+ this._haloWidth = haloWidth
+ this._delegate && (this._delegate.uniforms.haloWidth = haloWidth)
+ return this
+ }
+
+ get haloWidth() {
+ return this._haloWidth
+ }
+
+ set selected(selected) {
+ this._selected = selected
+ this._delegate && (this._delegate.selected = selected)
+ return this
+ }
+
+ get selected() {
+ return this._selected
+ }
+
+ /**
+ *
+ * @private
+ */
+ _createPostProcessStage() {
+ this._delegate = Cesium.PostProcessStageLibrary.createLensFlareStage()
+ if (this._delegate) {
+ this._delegate.uniforms.intensity = this._intensity
+ this._delegate.uniforms.distortion = this._distortion
+ this._delegate.uniforms.dirtAmount = this._dirtAmount
+ this._delegate.uniforms.haloWidth = this._haloWidth
+ this._viewer.scene.postProcessStages.add(this._delegate)
+ }
+ }
+
+ /**
+ *
+ * @param viewer
+ * @returns {LensFlare}
+ */
+ addTo(viewer) {
+ if (!viewer) {
+ return this
+ }
+ this._viewer = viewer
+ this._state = State.ADDED
+ return this
+ }
+}
+
+export default LensFlare
diff --git a/src/modules/effect/type/NightVision.js b/src/modules/effect/type/NightVision.js
new file mode 100644
index 00000000..d5d8eb5c
--- /dev/null
+++ b/src/modules/effect/type/NightVision.js
@@ -0,0 +1,69 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-14 23:10:14
+ */
+
+import { Cesium } from '../../../namespace'
+import State from '../../state/State'
+
+class NightVision {
+ constructor() {
+ this._enable = false
+ this._selected = []
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return 'night'
+ }
+
+ set enable(enable) {
+ this._enable = enable
+ if (enable && this._viewer && !this._delegate) {
+ this._createPostProcessStage()
+ }
+ this._delegate && (this._delegate.enabled = enable)
+ return this
+ }
+
+ get enable() {
+ return this._enable
+ }
+
+ set selected(selected) {
+ this._selected = selected
+ this._delegate && (this._delegate.selected = selected)
+ return this
+ }
+
+ get selected() {
+ return this._selected
+ }
+
+ /**
+ *
+ * @private
+ */
+ _createPostProcessStage() {
+ this._delegate = Cesium.PostProcessStageLibrary.createNightVisionStage()
+ if (this._delegate) {
+ this._viewer.scene.postProcessStages.add(this._delegate)
+ }
+ }
+
+ /**
+ *
+ * @param viewer
+ * @returns {NightVision}
+ */
+ addTo(viewer) {
+ if (!viewer) {
+ return this
+ }
+ this._viewer = viewer
+ this._state = State.ADDED
+ return this
+ }
+}
+
+export default NightVision
diff --git a/src/modules/effect/type/Silhouette.js b/src/modules/effect/type/Silhouette.js
new file mode 100644
index 00000000..958054b4
--- /dev/null
+++ b/src/modules/effect/type/Silhouette.js
@@ -0,0 +1,102 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-14 23:51:47
+ */
+
+import { Cesium } from '../../../namespace'
+import State from '../../state/State'
+
+class Silhouette {
+ constructor() {
+ this._viewer = undefined
+ this._delegate = undefined
+ this._enable = false
+ this._color = Cesium.Color.GREEN
+ this._length = 0.5
+ this._selected = []
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return 'silhouette'
+ }
+
+ set enable(enable) {
+ this._enable = enable
+ if (
+ enable &&
+ this._viewer &&
+ Cesium.PostProcessStageLibrary.isSilhouetteSupported(
+ this._viewer.scene
+ ) &&
+ !this._delegate
+ ) {
+ this._createPostProcessStage()
+ }
+ this._delegate && (this._delegate.enabled = enable)
+ return this
+ }
+
+ get enable() {
+ return this._enable
+ }
+
+ set color(color) {
+ this._color = color
+ this._delegate && (this._delegate.uniforms.color = color)
+ return this
+ }
+
+ get color() {
+ return this._color
+ }
+
+ set length(length) {
+ this._length = length
+ this._delegate && (this._delegate.uniforms.length = length)
+ return this
+ }
+
+ get length() {
+ return this._length
+ }
+
+ set selected(selected) {
+ this._selected = selected
+ this._delegate && (this._delegate.selected = selected)
+ return this
+ }
+
+ get selected() {
+ return this._selected
+ }
+
+ /**
+ *
+ * @private
+ */
+ _createPostProcessStage() {
+ this._delegate = Cesium.PostProcessStageLibrary.createSilhouetteStage()
+ if (this._delegate) {
+ this._delegate.uniforms.color = this._color
+ this._delegate.uniforms.length = this._length
+ this._viewer.scene.postProcessStages.add(this._delegate)
+ }
+ }
+
+ /**
+ *
+ * @param viewer
+ * @returns {Silhouette}
+ */
+ addTo(viewer) {
+ if (!viewer) {
+ return this
+ }
+ this._viewer = viewer
+ this._state = State.ADDED
+ return this
+ }
+}
+
+export default Silhouette
diff --git a/src/modules/effect/type/Sun.js b/src/modules/effect/type/Sun.js
new file mode 100644
index 00000000..c217d7ce
--- /dev/null
+++ b/src/modules/effect/type/Sun.js
@@ -0,0 +1,4 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-08-01 00:22:00
+ */
diff --git a/src/modules/event/Event.js b/src/modules/event/Event.js
new file mode 100644
index 00000000..4c3cbb43
--- /dev/null
+++ b/src/modules/event/Event.js
@@ -0,0 +1,128 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-02 15:24:38
+ */
+
+import { Cesium } from '../../namespace'
+
+class Event {
+ constructor(types) {
+ this._types = types
+ this._cache = {}
+ }
+
+ /**
+ * @param type
+ * @param callback
+ * @param context
+ * @returns {any}
+ * @private
+ */
+ _on(type, callback, context) {
+ let event = this.getEvent(type)
+ let removeCallback = undefined
+ if (event && callback) {
+ removeCallback = event.addEventListener(callback, context || this)
+ }
+ return removeCallback
+ }
+
+ /**
+ * @param type
+ * @param callback
+ * @param context
+ * @returns {boolean}
+ * @private
+ */
+ _off(type, callback, context) {
+ let event = this.getEvent(type)
+ let removed = false
+ if (event && callback) {
+ removed = event.removeEventListener(callback, context || this)
+ }
+ return removed
+ }
+
+ /**
+ * @param type
+ * @param params
+ * @private
+ */
+ _fire(type, params) {
+ let event = this.getEvent(type)
+ if (event) {
+ event.raiseEvent(params)
+ }
+ }
+
+ /**
+ * Event registration
+ * Subclasses need to override
+ *
+ */
+ _registerEvent() {
+ Object.keys(this._types).forEach((key) => {
+ let type = this._types[key]
+ this._cache[type] = new Cesium.Event()
+ })
+ }
+
+ /**
+ * Subscribe event
+ * @param type
+ * @param callback
+ * @param context
+ * @returns remove callback function
+ */
+ on(type, callback, context) {
+ return this._on(type, callback, context)
+ }
+
+ /**
+ * Subscribe once event
+ * @param type
+ * @param callback
+ * @param context
+ */
+ once(type, callback, context) {
+ let removeCallback = this._on(
+ type,
+ (e) => {
+ callback(e)
+ removeCallback && removeCallback()
+ },
+ context
+ )
+ }
+
+ /**
+ * Unsubscribe event
+ * @param type
+ * @param callback
+ * @param context
+ * @returns Boolean
+ */
+ off(type, callback, context) {
+ return this._off(type, callback, context)
+ }
+
+ /**
+ * Trigger subscription event
+ * @param type
+ * @param params
+ */
+ fire(type, params) {
+ this._fire(type, params)
+ }
+
+ /**
+ * Returns events by type
+ * @param type
+ * @returns Event
+ */
+ getEvent(type) {
+ return this._cache[type] || undefined
+ }
+}
+
+export default Event
diff --git a/src/modules/event/EventType.js b/src/modules/event/EventType.js
new file mode 100644
index 00000000..eb50bd1b
--- /dev/null
+++ b/src/modules/event/EventType.js
@@ -0,0 +1,120 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-04-10 17:02:28
+ */
+
+import { Cesium } from '../../namespace'
+
+const BaseEventType = {
+ ADD: 'add',
+ REMOVE: 'remove',
+}
+
+const MouseEventType = {
+ LEFT_DOWN: Cesium.ScreenSpaceEventType.LEFT_DOWN,
+ LEFT_UP: Cesium.ScreenSpaceEventType.LEFT_UP,
+ CLICK: Cesium.ScreenSpaceEventType.LEFT_CLICK,
+ RIGHT_DOWN: Cesium.ScreenSpaceEventType.RIGHT_DOWN,
+ RIGHT_UP: Cesium.ScreenSpaceEventType.RIGHT_UP,
+ RIGHT_CLICK: Cesium.ScreenSpaceEventType.RIGHT_CLICK,
+ DB_CLICK: Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK,
+ MOUSE_MOVE: Cesium.ScreenSpaceEventType.MOUSE_MOVE,
+ WHEEL: Cesium.ScreenSpaceEventType.WHEEL,
+ MOUSE_OVER: 'mouseover',
+ MOUSE_OUT: 'mouseout',
+}
+
+const ViewerEventType = {
+ ADD_LAYER: 'addLayer',
+ REMOVE_LAYER: 'removeLayer',
+ ADD_EFFECT: 'addEffect',
+ REMOVE_EFFECT: 'removeEffect',
+ LEFT_DOWN: Cesium.ScreenSpaceEventType.LEFT_DOWN,
+ LEFT_UP: Cesium.ScreenSpaceEventType.LEFT_UP,
+ CLICK: Cesium.ScreenSpaceEventType.LEFT_CLICK,
+ RIGHT_DOWN: Cesium.ScreenSpaceEventType.RIGHT_DOWN,
+ RIGHT_UP: Cesium.ScreenSpaceEventType.RIGHT_UP,
+ RIGHT_CLICK: Cesium.ScreenSpaceEventType.RIGHT_CLICK,
+ DB_CLICK: Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK,
+ MOUSE_MOVE: Cesium.ScreenSpaceEventType.MOUSE_MOVE,
+ WHEEL: Cesium.ScreenSpaceEventType.WHEEL,
+}
+
+const SceneEventType = {
+ CAMERA_MOVE_END: 'cameraMoveEnd',
+ CAMERA_CHANGED: 'cameraChanged',
+ PRE_UPDATE: 'preUpdate',
+ POST_UPDATE: 'postUpdate',
+ PRE_RENDER: 'preRender',
+ POST_RENDER: 'postRender',
+ MORPH_COMPLETE: 'morphComplete',
+ CLOCK_TICK: 'clockTick',
+ RENDER_ERROR: 'renderError',
+}
+
+const OverlayEventType = {
+ ...BaseEventType,
+ LEFT_DOWN: Cesium.ScreenSpaceEventType.LEFT_DOWN,
+ LEFT_UP: Cesium.ScreenSpaceEventType.LEFT_UP,
+ CLICK: Cesium.ScreenSpaceEventType.LEFT_CLICK,
+ RIGHT_DOWN: Cesium.ScreenSpaceEventType.RIGHT_DOWN,
+ RIGHT_UP: Cesium.ScreenSpaceEventType.RIGHT_UP,
+ RIGHT_CLICK: Cesium.ScreenSpaceEventType.RIGHT_CLICK,
+ DB_CLICK: Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK,
+ MOUSE_MOVE: Cesium.ScreenSpaceEventType.MOUSE_MOVE,
+ MOUSE_OVER: 'mouseover',
+ MOUSE_OUT: 'mouseout',
+ POSITION_UPDATE: 'positionUpdate',
+}
+
+const LayerGroupEventType = BaseEventType
+
+const LayerEventType = {
+ ...BaseEventType,
+ LEFT_DOWN: Cesium.ScreenSpaceEventType.LEFT_DOWN,
+ LEFT_UP: Cesium.ScreenSpaceEventType.LEFT_UP,
+ CLICK: Cesium.ScreenSpaceEventType.LEFT_CLICK,
+ RIGHT_DOWN: Cesium.ScreenSpaceEventType.RIGHT_DOWN,
+ RIGHT_UP: Cesium.ScreenSpaceEventType.RIGHT_UP,
+ RIGHT_CLICK: Cesium.ScreenSpaceEventType.RIGHT_CLICK,
+ DB_CLICK: Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK,
+}
+
+const TrackEventType = {
+ ...BaseEventType,
+ POST_RENDER: 'postRender',
+ ACTIVATE: 'activate',
+ DEACTIVATE: 'deactivate',
+ RESET_TIME_LINE: 'restTimeLine',
+}
+
+const PathEventType = {
+ ...BaseEventType,
+ POST_RENDER: 'postRender',
+ RESET_TIME_LINE: 'restTimeLine',
+}
+
+const PlotEventType = {
+ DRAW_START: 'drawStart',
+ DRAW_STOP: 'drawStop',
+ EDIT_START: 'editStart',
+ EDIT_STOP: 'editEnd',
+ DRAW_ANCHOR: 'drawAnchor',
+ CREATE_ANCHOR: 'createAnchor',
+ UPDATE_ANCHOR: 'updateAnchor',
+ ANCHOR_MOVING: 'anchorMoving',
+ EDIT_ANCHOR_STOP: 'editAnchorStop',
+ CLEAR_ANCHOR: 'clearAnchor',
+}
+
+export {
+ MouseEventType,
+ ViewerEventType,
+ SceneEventType,
+ LayerGroupEventType,
+ LayerEventType,
+ OverlayEventType,
+ TrackEventType,
+ PathEventType,
+ PlotEventType,
+}
diff --git a/src/modules/event/index.js b/src/modules/event/index.js
new file mode 100644
index 00000000..8672753f
--- /dev/null
+++ b/src/modules/event/index.js
@@ -0,0 +1,32 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-03-13 13:24:24
+ */
+
+export * from './EventType'
+export { default as Event } from './Event'
+
+/**
+ * scene
+ */
+export { default as MouseEvent } from './type/MouseEvent'
+export { default as ViewerEvent } from './type/ViewerEvent'
+export { default as SceneEvent } from './type/SceneEvent'
+
+/**
+ * layer
+ */
+export { default as LayerGroupEvent } from './type/LayerGroupEvent'
+export { default as LayerEvent } from './type/LayerEvent'
+export { default as OverlayEvent } from './type/OverlayEvent'
+
+/**
+ * animation
+ */
+export { default as TrackEvent } from './type/TrackEvent'
+export { default as PathEvent } from './type/PathEvent'
+
+/**
+ * plot
+ */
+export { default as PlotEvent } from './type/PlotEvent'
diff --git a/src/modules/event/type/LayerEvent.js b/src/modules/event/type/LayerEvent.js
new file mode 100644
index 00000000..d8cdf088
--- /dev/null
+++ b/src/modules/event/type/LayerEvent.js
@@ -0,0 +1,16 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-02 14:26:35
+ */
+
+import { LayerEventType } from '../EventType'
+import Event from '../Event'
+
+class LayerEvent extends Event {
+ constructor() {
+ super(LayerEventType)
+ this._registerEvent()
+ }
+}
+
+export default LayerEvent
diff --git a/src/modules/event/type/LayerGroupEvent.js b/src/modules/event/type/LayerGroupEvent.js
new file mode 100644
index 00000000..d9806803
--- /dev/null
+++ b/src/modules/event/type/LayerGroupEvent.js
@@ -0,0 +1,16 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-28 20:18:04
+ */
+
+import { LayerGroupEventType } from '../EventType'
+import Event from '../Event'
+
+class LayerGroupEvent extends Event {
+ constructor() {
+ super(LayerGroupEventType)
+ this._registerEvent()
+ }
+}
+
+export default LayerGroupEvent
diff --git a/src/modules/event/type/MouseEvent.js b/src/modules/event/type/MouseEvent.js
new file mode 100644
index 00000000..eda93fae
--- /dev/null
+++ b/src/modules/event/type/MouseEvent.js
@@ -0,0 +1,417 @@
+/**
+ * @Author: Caven
+ * @Date: 2019-12-31 16:58:31
+ */
+
+import { Cesium } from '../../../namespace'
+import { MouseEventType } from '../EventType'
+import Event from '../Event'
+
+/**
+ * Mouse events in 3D scene, optimized Cesium event model
+ */
+class MouseEvent extends Event {
+ constructor(viewer) {
+ super(MouseEventType)
+ this._viewer = viewer
+ this._selected = undefined
+ this._registerEvent()
+ this._addDefaultEvent()
+ }
+
+ /**
+ *
+ * @private
+ */
+ _registerEvent() {
+ let handler = new Cesium.ScreenSpaceEventHandler(this._viewer.canvas)
+ Object.keys(Cesium.ScreenSpaceEventType).forEach((key) => {
+ let type = Cesium.ScreenSpaceEventType[key]
+ this._cache[type] = new Cesium.Event()
+ handler.setInputAction((movement) => {
+ this._cache[type].raiseEvent(movement)
+ }, type)
+ })
+ }
+
+ /**
+ * add default event for the viewer
+ * @private
+ */
+ _addDefaultEvent() {
+ this.on(this._types.LEFT_DOWN, this._leftDownHandler, this)
+ this.on(this._types.LEFT_UP, this._leftUpHandler, this)
+ this.on(this._types.CLICK, this._clickHandler, this)
+ this.on(this._types.DB_CLICK, this._dbClickHandler, this)
+ this.on(this._types.RIGHT_DOWN, this._rightDownHandler, this)
+ this.on(this._types.RIGHT_UP, this._rightUpHandler, this)
+ this.on(this._types.RIGHT_CLICK, this._rightClickHandler, this)
+ this.on(this._types.MOUSE_MOVE, this._mouseMoveHandler, this)
+ this.on(this._types.WHEEL, this._mouseWheelHandler, this)
+ }
+
+ /**
+ *
+ * Gets the mouse information for the mouse event
+ * @param position
+ * @private
+ *
+ */
+ _getMouseInfo(position) {
+ let scene = this._viewer.scene
+ let target = scene.pick(position)
+ let cartesian = undefined
+ let surfaceCartesian = undefined
+ let wgs84Position = undefined
+ let wgs84SurfacePosition = undefined
+ if (scene.pickPositionSupported) {
+ cartesian = scene.pickPosition(position)
+ }
+ if (cartesian) {
+ let c = Cesium.Ellipsoid.WGS84.cartesianToCartographic(cartesian)
+ if (c) {
+ wgs84Position = {
+ lng: Cesium.Math.toDegrees(c.longitude),
+ lat: Cesium.Math.toDegrees(c.latitude),
+ alt: c.height,
+ }
+ }
+ }
+ if (
+ scene.mode === Cesium.SceneMode.SCENE3D &&
+ !(this._viewer.terrainProvider instanceof Cesium.EllipsoidTerrainProvider)
+ ) {
+ let ray = scene.camera.getPickRay(position)
+ surfaceCartesian = scene.globe.pick(ray, scene)
+ } else {
+ surfaceCartesian = scene.camera.pickEllipsoid(
+ position,
+ Cesium.Ellipsoid.WGS84
+ )
+ }
+ if (surfaceCartesian) {
+ let c = Cesium.Ellipsoid.WGS84.cartesianToCartographic(surfaceCartesian)
+ if (c) {
+ wgs84SurfacePosition = {
+ lng: Cesium.Math.toDegrees(c.longitude),
+ lat: Cesium.Math.toDegrees(c.latitude),
+ alt: c.height,
+ }
+ }
+ }
+
+ return {
+ target: target,
+ windowPosition: position,
+ position: cartesian,
+ wgs84Position: wgs84Position,
+ surfacePosition: surfaceCartesian,
+ wgs84SurfacePosition: wgs84SurfacePosition,
+ }
+ }
+
+ /**
+ * Gets the drill pick overlays for the mouse event
+ * @param position
+ * @returns {[]}
+ * @private
+ */
+ _getDrillInfos(position) {
+ let drillInfos = []
+ let scene = this._viewer.scene
+ let targets = scene.drillPick(position)
+ if (targets && targets.length) {
+ targets.forEach((target) => {
+ drillInfos.push(this._getTargetInfo(target))
+ })
+ }
+ return drillInfos
+ }
+
+ /**
+ * Return the Overlay id
+ * @param target
+ * @returns {any}
+ * @private
+ */
+ _getOverlayId(target) {
+ let overlayId = undefined
+
+ // for Entity
+ if (target?.id instanceof Cesium.Entity) {
+ overlayId = target.id.overlayId
+ }
+
+ // for Cesium3DTileFeature
+ else if (target instanceof Cesium.Cesium3DTileFeature) {
+ overlayId = target.tileset.overlayId
+ }
+
+ // for Cesium3DTileset
+ else if (target?.primitive instanceof Cesium.Cesium3DTileset) {
+ overlayId = target.primitive.overlayId
+ }
+
+ // for Primitve
+ else if (target?.primitive) {
+ overlayId = target.primitive.overlayId
+ }
+
+ return overlayId
+ }
+
+ /**
+ * Returns the target information for the mouse event
+ * @param target
+ * @returns {{instanceId: *, overlay: undefined, feature: undefined, layer: undefined}}
+ * @private
+ */
+ _getTargetInfo(target) {
+ let overlay = undefined
+ let layer = undefined
+ let feature = undefined
+
+ // for Entity
+ if (target?.id instanceof Cesium.Entity) {
+ layer = this._viewer
+ .getLayers()
+ .filter((item) => item.layerId === target.id.layerId)[0]
+ if (layer?.getOverlay) {
+ overlay = layer.getOverlay(target.id.overlayId)
+ }
+ }
+
+ // for Cesium3DTileFeature
+ else if (target instanceof Cesium.Cesium3DTileFeature) {
+ layer = this._viewer
+ .getLayers()
+ .filter((item) => item.layerId === target.tileset.layerId)[0]
+ feature = target
+ if (layer?.getOverlay) {
+ overlay = layer.getOverlay(target.tileset.overlayId)
+ if (feature && feature.getPropertyNames) {
+ let propertyNames = feature.getPropertyNames()
+ propertyNames.forEach((item) => {
+ overlay.attr[item] = feature.getProperty(item)
+ })
+ }
+ }
+ }
+
+ // for Cesium3DTileset
+ else if (target?.primitive instanceof Cesium.Cesium3DTileset) {
+ layer = this._viewer
+ .getLayers()
+ .filter((item) => item.layerId === target.primitive.layerId)[0]
+ if (layer?.getOverlay) {
+ overlay = layer.getOverlay(target.primitive.overlayId)
+ }
+ }
+
+ // for Primitve
+ else if (target?.primitive) {
+ layer = this._viewer
+ .getLayers()
+ .filter((item) => item.layerId === target.primitive.layerId)[0]
+ if (layer?.getOverlay) {
+ overlay = layer.getOverlay(target.primitive.overlayId)
+ }
+ }
+
+ return {
+ layer: layer,
+ overlay: overlay,
+ feature: feature,
+ instanceId: target?.instanceId,
+ }
+ }
+
+ /**
+ * Trigger subscription event
+ * @param type
+ * @param mouseInfo
+ * @private
+ */
+ _raiseEvent(type, mouseInfo = {}) {
+ let event = undefined
+ let targetInfo = this._getTargetInfo(mouseInfo.target)
+ let overlay = targetInfo?.overlay
+ let layer = targetInfo?.layer
+ // get Overlay Event
+ if (overlay?.overlayEvent) {
+ event = overlay.overlayEvent.getEvent(type)
+ }
+
+ // get Layer Event
+ if ((!event || event.numberOfListeners === 0) && layer?.layerEvent) {
+ event = layer.layerEvent.getEvent(type)
+ }
+
+ // get Viewer Event
+ if (
+ (!event || event.numberOfListeners === 0) &&
+ this._viewer?.viewerEvent
+ ) {
+ event = this._viewer.viewerEvent.getEvent(type)
+ }
+
+ event &&
+ event.numberOfListeners > 0 &&
+ event.raiseEvent({
+ ...targetInfo,
+ ...mouseInfo,
+ })
+
+ // get Drill Pick Event
+ if (overlay?.allowDrillPicking) {
+ let drillInfos = this._getDrillInfos(mouseInfo.windowPosition)
+ drillInfos.forEach((drillInfo) => {
+ let dillOverlay = drillInfo?.overlay
+ let dillLayer = drillInfo?.layer
+ if (
+ dillOverlay?.overlayId !== overlay.overlayId &&
+ dillOverlay?.overlayEvent
+ ) {
+ // get Overlay Event
+ event = dillOverlay.overlayEvent.getEvent(type)
+ // get Layer Event
+ if (
+ (!event || event.numberOfListeners === 0) &&
+ dillLayer?.layerEvent
+ ) {
+ event = dillLayer.layerEvent.getEvent(type)
+ }
+ event &&
+ event.numberOfListeners > 0 &&
+ event.raiseEvent({
+ ...drillInfo,
+ ...mouseInfo,
+ })
+ }
+ })
+ }
+ }
+
+ /**
+ * Default click event handler
+ * @param movement
+ * @returns {boolean}
+ * @private
+ */
+ _clickHandler(movement) {
+ if (!movement?.position) {
+ return false
+ }
+ let mouseInfo = this._getMouseInfo(movement.position)
+ this._raiseEvent(MouseEventType.CLICK, mouseInfo)
+ }
+
+ /**
+ * Default dbClick event handler
+ * @param movement
+ * @returns {boolean}
+ * @private
+ */
+ _dbClickHandler(movement) {
+ if (!movement?.position) {
+ return false
+ }
+ let mouseInfo = this._getMouseInfo(movement.position)
+ this._raiseEvent(MouseEventType.DB_CLICK, mouseInfo)
+ }
+
+ /**
+ * Default rightClick event handler
+ * @param movement
+ * @returns {boolean}
+ * @private
+ */
+ _rightClickHandler(movement) {
+ if (!movement?.position) {
+ return false
+ }
+ let mouseInfo = this._getMouseInfo(movement.position)
+ this._raiseEvent(MouseEventType.RIGHT_CLICK, mouseInfo)
+ }
+
+ /**
+ * Default mousemove event handler
+ * @param movement
+ * @returns {boolean}
+ * @private
+ */
+ _mouseMoveHandler(movement) {
+ if (!movement?.endPosition) {
+ return false
+ }
+ let mouseInfo = this._getMouseInfo(movement.endPosition)
+ this._viewer.canvas.style.cursor = mouseInfo.target ? 'pointer' : 'default'
+ this._raiseEvent(MouseEventType.MOUSE_MOVE, mouseInfo)
+
+ // add event for overlay
+ if (
+ !this._selected ||
+ this._getOverlayId(this._selected.target) !==
+ this._getOverlayId(mouseInfo.target)
+ ) {
+ this._raiseEvent(MouseEventType.MOUSE_OUT, this._selected)
+ this._raiseEvent(MouseEventType.MOUSE_OVER, mouseInfo)
+ this._selected = mouseInfo
+ }
+ }
+
+ /**
+ * Default mouse left down event handler
+ * @param movement
+ * @private
+ */
+ _leftDownHandler(movement) {
+ if (!movement?.position) {
+ return false
+ }
+ let mouseInfo = this._getMouseInfo(movement.position)
+ this._raiseEvent(MouseEventType.LEFT_DOWN, mouseInfo)
+ }
+
+ /**
+ * Default mouse left up event handler
+ * @param movement
+ * @private
+ */
+ _leftUpHandler(movement) {
+ this._raiseEvent(MouseEventType.LEFT_UP, { movement })
+ }
+
+ /**
+ * Default mouse right down event handler
+ * @param movement
+ * @private
+ */
+ _rightDownHandler(movement) {
+ if (!movement?.position) {
+ return false
+ }
+ let mouseInfo = this._getMouseInfo(movement.position)
+ this._raiseEvent(MouseEventType.RIGHT_DOWN, mouseInfo)
+ }
+
+ /**
+ * Default mouse right up event handler
+ * @param movement
+ * @private
+ */
+ _rightUpHandler(movement) {
+ this._raiseEvent(MouseEventType.RIGHT_UP, { movement })
+ }
+
+ /**
+ * Default mouse wheel event handler
+ * @param movement
+ * @private
+ */
+ _mouseWheelHandler(movement) {
+ this._raiseEvent(MouseEventType.WHEEL, { movement })
+ }
+}
+
+export default MouseEvent
diff --git a/src/modules/event/type/OverlayEvent.js b/src/modules/event/type/OverlayEvent.js
new file mode 100644
index 00000000..49b35b2d
--- /dev/null
+++ b/src/modules/event/type/OverlayEvent.js
@@ -0,0 +1,17 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-02 14:26:35
+ */
+
+import { Cesium } from '../../../namespace'
+import { OverlayEventType } from '../EventType'
+import Event from '../Event'
+
+class OverlayEvent extends Event {
+ constructor() {
+ super(OverlayEventType)
+ this._registerEvent()
+ }
+}
+
+export default OverlayEvent
diff --git a/src/modules/event/type/PathEvent.js b/src/modules/event/type/PathEvent.js
new file mode 100644
index 00000000..60e25f83
--- /dev/null
+++ b/src/modules/event/type/PathEvent.js
@@ -0,0 +1,17 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-05-11 23:28:13
+ */
+
+import { Cesium } from '../../../namespace'
+import { PathEventType } from '../EventType'
+import Event from '../Event'
+
+class PathEvent extends Event {
+ constructor() {
+ super(PathEventType)
+ this._registerEvent()
+ }
+}
+
+export default PathEvent
diff --git a/src/modules/event/type/PlotEvent.js b/src/modules/event/type/PlotEvent.js
new file mode 100644
index 00000000..be041199
--- /dev/null
+++ b/src/modules/event/type/PlotEvent.js
@@ -0,0 +1,16 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-05-11 23:28:13
+ */
+
+import { PlotEventType } from '../EventType'
+import Event from '../Event'
+
+class PlotEvent extends Event {
+ constructor() {
+ super(PlotEventType)
+ this._registerEvent()
+ }
+}
+
+export default PlotEvent
diff --git a/src/modules/event/type/SceneEvent.js b/src/modules/event/type/SceneEvent.js
new file mode 100644
index 00000000..fdd16bfe
--- /dev/null
+++ b/src/modules/event/type/SceneEvent.js
@@ -0,0 +1,159 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-03-02 21:32:43
+ */
+
+import { SceneEventType } from '../EventType'
+import Event from '../Event'
+
+class SceneEvent extends Event {
+ constructor(viewer) {
+ super(SceneEventType)
+ this._camera = viewer.camera
+ this._scene = viewer.scene
+ this._clock = viewer.clock
+ }
+
+ /**
+ * Subscribe event
+ * @param type
+ * @param callback
+ * @param context
+ * @returns {any}
+ */
+ on(type, callback, context) {
+ let removeCallback = undefined
+ switch (type) {
+ case SceneEventType.CAMERA_MOVE_END:
+ removeCallback = this._camera.moveEnd.addEventListener(
+ callback,
+ context || this
+ )
+ break
+ case SceneEventType.CAMERA_CHANGED:
+ removeCallback = this._camera.changed.addEventListener(
+ callback,
+ context || this
+ )
+ break
+ case SceneEventType.PRE_UPDATE:
+ removeCallback = this._scene.preUpdate.addEventListener(
+ callback,
+ context || this
+ )
+ break
+ case SceneEventType.POST_UPDATE:
+ removeCallback = this._scene.postUpdate.addEventListener(
+ callback,
+ context || this
+ )
+ break
+ case SceneEventType.PRE_RENDER:
+ removeCallback = this._scene.preRender.addEventListener(
+ callback,
+ context || this
+ )
+ break
+ case SceneEventType.POST_RENDER:
+ removeCallback = this._scene.postRender.addEventListener(
+ callback,
+ context || this
+ )
+ break
+ case SceneEventType.MORPH_COMPLETE:
+ removeCallback = this._scene.morphComplete.addEventListener(
+ callback,
+ context || this
+ )
+ break
+ case SceneEventType.CLOCK_TICK:
+ removeCallback = this._clock.onTick.addEventListener(
+ callback,
+ context || this
+ )
+ break
+ case SceneEventType.RENDER_ERROR:
+ removeCallback = this._scene.renderError.addEventListener(
+ callback,
+ context || this
+ )
+ break
+ default:
+ break
+ }
+ return removeCallback
+ }
+
+ /**
+ * Unsubscribe event
+ * @param type
+ * @param callback
+ * @param context
+ * @returns {boolean}
+ */
+ off(type, callback, context) {
+ let removed = false
+ switch (type) {
+ case SceneEventType.CAMERA_MOVE_END:
+ removed = this._camera.moveEnd.removeEventListener(
+ callback,
+ context || this
+ )
+ break
+ case SceneEventType.CAMERA_CHANGED:
+ removed = this._camera.changed.removeEventListener(
+ callback,
+ context || this
+ )
+ break
+ case SceneEventType.PRE_UPDATE:
+ removed = this._scene.preUpdate.removeEventListener(
+ callback,
+ context || this
+ )
+ break
+ case SceneEventType.POST_UPDATE:
+ removed = this._scene.postUpdate.removeEventListener(
+ callback,
+ context || this
+ )
+ break
+ case SceneEventType.PRE_RENDER:
+ removed = this._scene.preRender.removeEventListener(
+ callback,
+ context || this
+ )
+ break
+ case SceneEventType.POST_RENDER:
+ removed = this._scene.postRender.removeEventListener(
+ callback,
+ context || this
+ )
+ break
+ case SceneEventType.MORPH_COMPLETE:
+ removed = this._scene.morphComplete.removeEventListener(
+ callback,
+ context || this
+ )
+ break
+ case SceneEventType.CLOCK_TICK:
+ removed = this._clock.onTick.removeEventListener(
+ callback,
+ context || this
+ )
+ break
+ case SceneEventType.RENDER_ERROR:
+ removed = this._scene.renderError.removeEventListener(
+ callback,
+ context || this
+ )
+ break
+ default:
+ break
+ }
+
+ return removed
+ }
+}
+
+export default SceneEvent
diff --git a/src/modules/event/type/TrackEvent.js b/src/modules/event/type/TrackEvent.js
new file mode 100644
index 00000000..67e0dc11
--- /dev/null
+++ b/src/modules/event/type/TrackEvent.js
@@ -0,0 +1,16 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-06-08 20:37:28
+ */
+
+import { TrackEventType } from '../EventType'
+import Event from '../Event'
+
+class TrackEvent extends Event {
+ constructor() {
+ super(TrackEventType)
+ this._registerEvent()
+ }
+}
+
+export default TrackEvent
diff --git a/src/modules/event/type/ViewerEvent.js b/src/modules/event/type/ViewerEvent.js
new file mode 100644
index 00000000..fade3ac3
--- /dev/null
+++ b/src/modules/event/type/ViewerEvent.js
@@ -0,0 +1,16 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-02 14:26:35
+ */
+
+import { ViewerEventType } from '../EventType'
+import Event from '../Event'
+
+class ViewerEvent extends Event {
+ constructor() {
+ super(ViewerEventType)
+ this._registerEvent()
+ }
+}
+
+export default ViewerEvent
diff --git a/src/modules/exts/BaseLayerPicker.js b/src/modules/exts/BaseLayerPicker.js
new file mode 100644
index 00000000..1ea404ee
--- /dev/null
+++ b/src/modules/exts/BaseLayerPicker.js
@@ -0,0 +1,118 @@
+/**
+ @Author: Caven Chen
+ **/
+
+import { Cesium } from '../../namespace'
+
+const { EllipsoidTerrainProvider } = Cesium
+
+class BaseLayerPicker {
+ constructor(options) {
+ if (!options.globe) {
+ throw new Error('globe is required')
+ }
+ this._globe = options.globe
+ this._imageryLayers = []
+ this._terrainProviders = []
+ this._selectedImageryLayer = undefined
+ this._selectedTerrain = undefined
+ this._count = 0
+ }
+
+ set selectedImageryLayer(imageryLayer) {
+ if (!imageryLayer || !imageryLayer.layers) {
+ new Error('imagery format error')
+ }
+ let imageryLayers = this._globe.imageryLayers
+ if (!this._selectedImageryLayer) {
+ for (let i = imageryLayer.layers.length - 1; i >= 0; i--) {
+ imageryLayers.add(imageryLayer.layers[i], 0)
+ }
+ } else if (
+ this._selectedImageryLayer &&
+ imageryLayer.id !== this._selectedImageryLayer.id
+ ) {
+ imageryLayers.removeAll()
+ for (let i = imageryLayer.layers.length - 1; i >= 0; i--) {
+ imageryLayers.addImageryProvider(imageryLayer.layers[i], 0)
+ }
+ }
+ this._selectedImageryLayer = imageryLayer
+ }
+
+ get selectedImageryLayer() {
+ return this._selectedImageryLayer
+ }
+
+ set selectedTerrain(terrain) {
+ if (this.selectedTerrain !== terrain) {
+ this._globe.depthTestAgainstTerrain = !(
+ terrain instanceof EllipsoidTerrainProvider
+ )
+ this._globe.terrainProvider = terrain
+ this._selectedTerrain = terrain
+ }
+ }
+
+ get selectedTerrain() {
+ return this._selectedTerrain
+ }
+
+ /**
+ *
+ * @param imageryLayer
+ * @returns {BaseLayerPicker}
+ */
+ addImageryLayer(imageryLayer) {
+ let imageryLayers = []
+ if (Array.isArray(imageryLayer)) {
+ imageryLayers = imageryLayer.slice(0)
+ } else {
+ imageryLayers = [imageryLayer]
+ }
+ this._count++
+ this._imageryLayers.push({
+ id: `dc-imagery-${this._count}`,
+ layers: imageryLayers,
+ })
+ return this
+ }
+
+ /**
+ *
+ * @param provider
+ * @returns {BaseLayerPicker}
+ */
+ addTerrainProvider(provider) {
+ this._terrainProviders.push(provider)
+ return this
+ }
+
+ /**
+ *
+ * @param index
+ * @returns {BaseLayerPicker}
+ */
+ changeImageryLayer(index) {
+ if (index > this._imageryLayers.length - 1) {
+ throw new Error('index error')
+ }
+ this.selectedImageryLayer = this._imageryLayers[index]
+ return this
+ }
+
+ /**
+ *
+ * @param index
+ * @returns {BaseLayerPicker}
+ */
+ changeTerrain(index) {
+ if (index > this._terrainProviders.length - 1) {
+ throw new Error('index error')
+ }
+ this.selectedTerrain = this._terrainProviders[index]
+ return this
+ }
+}
+
+export default BaseLayerPicker
diff --git a/src/modules/exts/GroundSkyBox.js b/src/modules/exts/GroundSkyBox.js
new file mode 100644
index 00000000..79bc4237
--- /dev/null
+++ b/src/modules/exts/GroundSkyBox.js
@@ -0,0 +1,189 @@
+/**
+ @author : Caven Chen
+ @date : 2023-05-09
+ */
+
+import { Cesium } from '../../namespace'
+const {
+ BoxGeometry,
+ Cartesian3,
+ defined,
+ DeveloperError,
+ GeometryPipeline,
+ Matrix3,
+ Matrix4,
+ Transforms,
+ VertexFormat,
+ BufferUsage,
+ CubeMap,
+ loadCubeMap,
+ RenderState,
+ VertexArray,
+ BlendingState,
+ SceneMode,
+ ShaderProgram,
+ ShaderSource,
+ SkyBox,
+} = Cesium
+
+const SkyBoxFS = `
+ uniform samplerCube u_cubeMap;
+ in vec3 v_texCoord;
+ void main()
+ {
+ vec4 color = czm_textureCube(u_cubeMap, normalize(v_texCoord));
+ out_FragColor = vec4(czm_gammaCorrect(color).rgb, czm_morphTime);
+ }
+`
+
+const SkyBoxVS = `
+ in vec3 position;
+ out vec3 v_texCoord;
+ uniform mat3 u_rotateMatrix;
+ void main()
+ {
+ vec3 p = czm_viewRotation * u_rotateMatrix * (czm_temeToPseudoFixed * (czm_entireFrustum.y * position));
+ gl_Position = czm_projection * vec4(p, 1.0);
+ v_texCoord = position.xyz;
+ }
+`
+
+class GroundSkyBox extends SkyBox {
+ constructor(options = {}) {
+ super(options)
+ this.offsetAngle = options?.offsetAngle || 0
+ }
+
+ update(frameState, useHdr) {
+ const that = this
+
+ if (!this.show) {
+ return undefined
+ }
+
+ if (
+ frameState.mode !== SceneMode.SCENE3D &&
+ frameState.mode !== SceneMode.MORPHING
+ ) {
+ return undefined
+ }
+
+ if (!frameState.passes.render) {
+ return undefined
+ }
+
+ const context = frameState.context
+
+ if (this._sources !== this.sources) {
+ this._sources = this.sources
+ const sources = this.sources
+
+ if (
+ !defined(sources.positiveX) ||
+ !defined(sources.negativeX) ||
+ !defined(sources.positiveY) ||
+ !defined(sources.negativeY) ||
+ !defined(sources.positiveZ) ||
+ !defined(sources.negativeZ)
+ ) {
+ throw new DeveloperError(
+ 'this.sources is required and must have positiveX, negativeX, positiveY, negativeY, positiveZ, and negativeZ properties.'
+ )
+ }
+
+ if (
+ typeof sources.positiveX !== typeof sources.negativeX ||
+ typeof sources.positiveX !== typeof sources.positiveY ||
+ typeof sources.positiveX !== typeof sources.negativeY ||
+ typeof sources.positiveX !== typeof sources.positiveZ ||
+ typeof sources.positiveX !== typeof sources.negativeZ
+ ) {
+ throw new DeveloperError(
+ 'this.sources properties must all be the same type.'
+ )
+ }
+
+ if (typeof sources.positiveX === 'string') {
+ // Given urls for cube-map images. Load them.
+ loadCubeMap(context, this._sources).then(function (cubeMap) {
+ that._cubeMap = that._cubeMap && that._cubeMap.destroy()
+ that._cubeMap = cubeMap
+ })
+ } else {
+ this._cubeMap = this._cubeMap && this._cubeMap.destroy()
+ this._cubeMap = new CubeMap({
+ context: context,
+ source: sources,
+ })
+ }
+ }
+
+ const command = this._command
+
+ command.modelMatrix = Transforms.eastNorthUpToFixedFrame(
+ frameState.camera.positionWC
+ )
+
+ if (this.offsetAngle !== 0) {
+ Matrix4.multiply(
+ command.modelMatrix,
+ Matrix4.fromRotationTranslation(
+ Matrix3.fromRotationZ((this.offsetAngle / 180) * Math.PI)
+ ),
+ command.modelMatrix
+ )
+ }
+
+ if (!defined(command.vertexArray)) {
+ command.uniformMap = {
+ u_cubeMap: function () {
+ return that._cubeMap
+ },
+ u_rotateMatrix: function () {
+ return Matrix4.getMatrix3(command.modelMatrix, new Matrix3())
+ },
+ }
+
+ const geometry = BoxGeometry.createGeometry(
+ BoxGeometry.fromDimensions({
+ dimensions: new Cartesian3(2.0, 2.0, 2.0),
+ vertexFormat: VertexFormat.POSITION_ONLY,
+ })
+ )
+ const attributeLocations = (this._attributeLocations =
+ GeometryPipeline.createAttributeLocations(geometry))
+
+ command.vertexArray = VertexArray.fromGeometry({
+ context: context,
+ geometry: geometry,
+ attributeLocations: attributeLocations,
+ bufferUsage: BufferUsage._DRAW,
+ })
+
+ command.renderState = RenderState.fromCache({
+ blending: BlendingState.ALPHA_BLEND,
+ })
+ }
+
+ if (!defined(command.shaderProgram) || this._useHdr !== useHdr) {
+ const fs = new ShaderSource({
+ defines: [useHdr ? 'HDR' : ''],
+ sources: [SkyBoxFS],
+ })
+ command.shaderProgram = ShaderProgram.fromCache({
+ context: context,
+ vertexShaderSource: SkyBoxVS,
+ fragmentShaderSource: fs,
+ attributeLocations: this._attributeLocations,
+ })
+ this._useHdr = useHdr
+ }
+
+ if (!defined(this._cubeMap)) {
+ return undefined
+ }
+ return command
+ }
+}
+
+export default GroundSkyBox
diff --git a/src/modules/exts/Viewer.js b/src/modules/exts/Viewer.js
new file mode 100644
index 00000000..a50e67bb
--- /dev/null
+++ b/src/modules/exts/Viewer.js
@@ -0,0 +1,1435 @@
+/**
+ @author : Caven Chen
+ @date : 2023-05-23
+ */
+import { Cesium } from '../../namespace'
+
+const {
+ BoundingSphere,
+ BoundingSphereState,
+ Cartographic,
+ CesiumWidget,
+ Cesium3DTileset,
+ Clock,
+ computeFlyToLocationForRectangle,
+ DataSourceCollection,
+ DataSourceDisplay,
+ defaultValue,
+ defined,
+ destroyObject,
+ DeveloperError,
+ Entity,
+ EntityView,
+ Event,
+ EventHelper,
+ getElement,
+ HeadingPitchRange,
+ ImageryLayer,
+ Matrix4,
+ Property,
+ SceneMode,
+ TimeDynamicPointCloud,
+ VoxelPrimitive,
+ Color,
+} = Cesium
+
+const boundingSphereScratch = new BoundingSphere()
+
+function trackDataSourceClock(timeline, clock, dataSource) {
+ if (defined(dataSource)) {
+ const dataSourceClock = dataSource.clock
+ if (defined(dataSourceClock)) {
+ dataSourceClock.getValue(clock)
+ }
+ }
+}
+
+/**
+ * @typedef {object} Viewer.ConstructorOptions
+ *
+ * Initialization options for the Viewer constructor
+ *
+ * @property {ImageryProvider|false} [imageryProvider=createWorldImagery()] The imagery provider to use. This value is only valid if `baseLayerPicker` is set to false. Deprecated.
+ * @property {ImageryLayer|false} [baseLayer=ImageryLayer.fromWorldImagery()] The bottommost imagery layer applied to the globe. If set to false
, no imagery provider will be added. This value is only valid if `baseLayerPicker` is set to false.
+ * @property {TerrainProvider} [terrainProvider=new EllipsoidTerrainProvider()] The terrain provider to use
+ * @property {Terrain} [terrain] A terrain object which handles asynchronous terrain provider. Can only specify if options.terrainProvider is undefined.
+ * @property {SkyBox|false} [skyBox] The skybox used to render the stars. When undefined
, the default stars are used. If set to false
, no skyBox, Sun, or Moon will be added.
+ * @property {SkyAtmosphere|false} [skyAtmosphere] Blue sky, and the glow around the Earth's limb. Set to false
to turn it off.
+ * @property {Element|string} [fullscreenElement=document.body] The element or id to be placed into fullscreen mode when the full screen button is pressed.
+ * @property {boolean} [useDefaultRenderLoop=true] True if this widget should control the render loop, false otherwise.
+ * @property {number} [targetFrameRate] The target frame rate when using the default render loop.
+ * @property {boolean} [showRenderLoopErrors=true] If true, this widget will automatically display an HTML panel to the user containing the error, if a render loop error occurs.
+ * @property {boolean} [useBrowserRecommendedResolution=true] If true, render at the browser's recommended resolution and ignore window.devicePixelRatio
.
+ * @property {boolean} [automaticallyTrackDataSourceClocks=true] If true, this widget will automatically track the clock settings of newly added DataSources, updating if the DataSource's clock changes. Set this to false if you want to configure the clock independently.
+ * @property {ContextOptions} [contextOptions] Context and WebGL creation properties passed to {@link Scene}.
+ * @property {SceneMode} [sceneMode=SceneMode.SCENE3D] The initial scene mode.
+ * @property {MapProjection} [mapProjection=new GeographicProjection()] The map projection to use in 2D and Columbus View modes.
+ * @property {Globe|false} [globe=new Globe(mapProjection.ellipsoid)] The globe to use in the scene. If set to false
, no globe will be added.
+ * @property {boolean} [orderIndependentTranslucency=true] If true and the configuration supports it, use order independent translucency.
+ * @property {Element|string} [creditContainer] The DOM element or ID that will contain the {@link CreditDisplay}. If not specified, the credits are added to the bottom of the widget itself.
+ * @property {Element|string} [creditViewport] The DOM element or ID that will contain the credit pop up created by the {@link CreditDisplay}. If not specified, it will appear over the widget itself.
+ * @property {DataSourceCollection} [dataSources=new DataSourceCollection()] The collection of data sources visualized by the widget. If this parameter is provided,
+ * the instance is assumed to be owned by the caller and will not be destroyed when the viewer is destroyed.
+ * @property {boolean} [shadows=false] Determines if shadows are cast by light sources.
+ * @property {ShadowMode} [terrainShadows=ShadowMode.RECEIVE_ONLY] Determines if the terrain casts or receives shadows from light sources.
+ * @property {MapMode2D} [mapMode2D=MapMode2D.INFINITE_SCROLL] Determines if the 2D map is rotatable or can be scrolled infinitely in the horizontal direction.
+ * @property {boolean} [projectionPicker=false] If set to true, the ProjectionPicker widget will be created.
+ * @property {boolean} [blurActiveElementOnCanvasFocus=true] If true, the active element will blur when the viewer's canvas is clicked. Setting this to false is useful for cases when the canvas is clicked only for retrieving position or an entity data without actually meaning to set the canvas to be the active element.
+ * @property {boolean} [requestRenderMode=false] If true, rendering a frame will only occur when needed as determined by changes within the scene. Enabling reduces the CPU/GPU usage of your application and uses less battery on mobile, but requires using {@link Scene#requestRender} to render a new frame explicitly in this mode. This will be necessary in many cases after making changes to the scene in other parts of the API. See {@link https://cesium.com/blog/2018/01/24/cesium-scene-rendering-performance/|Improving Performance with Explicit Rendering}.
+ * @property {number} [maximumRenderTimeChange=0.0] If requestRenderMode is true, this value defines the maximum change in simulation time allowed before a render is requested. See {@link https://cesium.com/blog/2018/01/24/cesium-scene-rendering-performance/|Improving Performance with Explicit Rendering}.
+ * @property {number} [depthPlaneEllipsoidOffset=0.0] Adjust the DepthPlane to address rendering artefacts below ellipsoid zero elevation.
+ * @property {number} [msaaSamples=1] If provided, this value controls the rate of multisample antialiasing. Typical multisampling rates are 2, 4, and sometimes 8 samples per pixel. Higher sampling rates of MSAA may impact performance in exchange for improved visual quality. This value only applies to WebGL2 contexts that support multisample render targets.
+ */
+
+/**
+ * A base widget for building applications. It composites all of the standard Cesium widgets into one reusable package.
+ * The widget can always be extended by using mixins, which add functionality useful for a variety of applications.
+ *
+ * @alias Viewer
+ * @constructor
+ *
+ * @param {Element|string} container The DOM element or ID that will contain the widget.
+ * @param {Viewer.ConstructorOptions} [options] Object describing initialization options
+ *
+ * @exception {DeveloperError} Element with id "container" does not exist in the document.
+ * @exception {DeveloperError} options.selectedImageryProviderViewModel is not available when not using the BaseLayerPicker widget, specify options.baseLayer instead.
+ * @exception {DeveloperError} options.selectedTerrainProviderViewModel is not available when not using the BaseLayerPicker widget, specify options.terrainProvider instead.
+ *
+ * @see Animation
+ * @see BaseLayerPicker
+ * @see CesiumWidget
+ * @see FullscreenButton
+ * @see HomeButton
+ * @see SceneModePicker
+ * @see Timeline
+ * @see viewerDragDropMixin
+ *
+ * @demo {@link https://sandcastle.cesium.com/index.html?src=Hello%20World.html|Cesium Sandcastle Hello World Demo}
+ *
+ * @example
+ * // Initialize the viewer widget with several custom options and mixins.
+ * try {
+ * const viewer = new Cesium.Viewer("cesiumContainer", {
+ * // Start in Columbus Viewer
+ * sceneMode: Cesium.SceneMode.COLUMBUS_VIEW,
+ * // Use Cesium World Terrain
+ * terrain: Cesium.Terrain.fromWorldTerrain(),
+ * // Use OpenStreetMaps
+ * baseLayer: new Cesium.ImageryLayer(OpenStreetMapImageryProvider({
+ * url: "https://a.tile.openstreetmap.org/"
+ * })),
+ * skyBox: new Cesium.SkyBox({
+ * sources: {
+ * positiveX: "stars/TychoSkymapII.t3_08192x04096_80_px.jpg",
+ * negativeX: "stars/TychoSkymapII.t3_08192x04096_80_mx.jpg",
+ * positiveY: "stars/TychoSkymapII.t3_08192x04096_80_py.jpg",
+ * negativeY: "stars/TychoSkymapII.t3_08192x04096_80_my.jpg",
+ * positiveZ: "stars/TychoSkymapII.t3_08192x04096_80_pz.jpg",
+ * negativeZ: "stars/TychoSkymapII.t3_08192x04096_80_mz.jpg"
+ * }
+ * }),
+ * // Show Columbus View map with Web Mercator projection
+ * mapProjection: new Cesium.WebMercatorProjection()
+ * });
+ * } catch (error) {
+ * console.log(error);
+ * }
+ *
+ * // Add basic drag and drop functionality
+ * viewer.extend(Cesium.viewerDragDropMixin);
+ *
+ * // Show a pop-up alert if we encounter an error when processing a dropped file
+ * viewer.dropError.addEventListener(function(dropHandler, name, error) {
+ * console.log(error);
+ * window.alert(error);
+ * });
+ */
+function Viewer(container, options) {
+ //>>includeStart('debug', pragmas.debug);
+ if (!defined(container)) {
+ throw new DeveloperError('container is required.')
+ }
+ //>>includeEnd('debug');
+
+ container = getElement(container)
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT)
+
+ const viewerContainer = document.createElement('div')
+
+ const bottomContainer = document.createElement('div')
+
+ const scene3DOnly = defaultValue(options.scene3DOnly, false)
+
+ let clock = new Clock()
+
+ if (defined(options.shouldAnimate)) {
+ clock.shouldAnimate = options.shouldAnimate
+ }
+
+ // Cesium widget
+ const cesiumWidget = new CesiumWidget(container, {
+ baseLayer: false,
+ clock: clock,
+ skyBox: options.skyBox,
+ skyAtmosphere: options.skyAtmosphere,
+ sceneMode: options.sceneMode,
+ mapProjection: options.mapProjection,
+ globe: options.globe,
+ orderIndependentTranslucency: options.orderIndependentTranslucency,
+ contextOptions: options.contextOptions,
+ useDefaultRenderLoop: options.useDefaultRenderLoop,
+ targetFrameRate: options.targetFrameRate,
+ showRenderLoopErrors: options.showRenderLoopErrors,
+ useBrowserRecommendedResolution: options.useBrowserRecommendedResolution,
+ scene3DOnly: scene3DOnly,
+ shadows: options.shadows,
+ terrainShadows: options.terrainShadows,
+ mapMode2D: options.mapMode2D,
+ blurActiveElementOnCanvasFocus: options.blurActiveElementOnCanvasFocus,
+ requestRenderMode: options.requestRenderMode,
+ maximumRenderTimeChange: options.maximumRenderTimeChange,
+ depthPlaneEllipsoidOffset: options.depthPlaneEllipsoidOffset,
+ msaaSamples: options.msaaSamples,
+ })
+
+ cesiumWidget.scene.backgroundColor = Color.TRANSPARENT
+
+ while (
+ cesiumWidget.creditViewport.hasChildNodes() &&
+ !(cesiumWidget.creditViewport.lastChild instanceof HTMLCanvasElement)
+ ) {
+ cesiumWidget.creditViewport.removeChild(
+ cesiumWidget.creditViewport.lastChild
+ )
+ }
+
+ cesiumWidget.creditViewport.className = 'dc-viewer-canvas'
+
+ let dataSourceCollection = options.dataSources
+ let destroyDataSourceCollection = false
+ if (!defined(dataSourceCollection)) {
+ dataSourceCollection = new DataSourceCollection()
+ destroyDataSourceCollection = true
+ }
+
+ const scene = cesiumWidget.scene
+
+ const dataSourceDisplay = new DataSourceDisplay({
+ scene: scene,
+ dataSourceCollection: dataSourceCollection,
+ })
+
+ const eventHelper = new EventHelper()
+
+ eventHelper.add(clock.onTick, Viewer.prototype._onTick, this)
+ eventHelper.add(scene.morphStart, Viewer.prototype._clearTrackedObject, this)
+
+ // Main Toolbar
+ const toolbar = document.createElement('div')
+
+ //Assign all properties to this instance. No "this" assignments should
+ //take place above this line.
+ this._dataSourceChangedListeners = {}
+ this._automaticallyTrackDataSourceClocks = defaultValue(
+ options.automaticallyTrackDataSourceClocks,
+ true
+ )
+
+ this._clock = clock
+ this._container = container
+ this._bottomContainer = bottomContainer
+ this._element = viewerContainer
+ this._cesiumWidget = cesiumWidget
+ this._dataSourceCollection = dataSourceCollection
+ this._destroyDataSourceCollection = destroyDataSourceCollection
+ this._dataSourceDisplay = dataSourceDisplay
+ this._toolbar = toolbar
+ this._eventHelper = eventHelper
+ this._lastWidth = 0
+ this._lastHeight = 0
+ this._allowDataSourcesToSuspendAnimation = true
+ this._entityView = undefined
+ this._enableInfoOrSelection = false
+ this._clockTrackedDataSource = undefined
+ this._trackedEntity = undefined
+ this._needTrackedEntityUpdate = false
+ this._selectedEntity = undefined
+ this._zoomIsFlight = false
+ this._zoomTarget = undefined
+ this._zoomPromise = undefined
+ this._zoomOptions = undefined
+ this._selectedEntityChanged = new Event()
+ this._trackedEntityChanged = new Event()
+
+ //Listen to data source events in order to track clock changes.
+ eventHelper.add(
+ dataSourceCollection.dataSourceAdded,
+ Viewer.prototype._onDataSourceAdded,
+ this
+ )
+ eventHelper.add(
+ dataSourceCollection.dataSourceRemoved,
+ Viewer.prototype._onDataSourceRemoved,
+ this
+ )
+
+ // Prior to each render, check if anything needs to be resized.
+ eventHelper.add(scene.postUpdate, Viewer.prototype.resize, this)
+ eventHelper.add(scene.postRender, Viewer.prototype._postRender, this)
+
+ // We need to subscribe to the data sources and collections so that we can clear the
+ // tracked object when it is removed from the scene.
+ // Subscribe to current data sources
+ const dataSourceLength = dataSourceCollection.length
+ for (let i = 0; i < dataSourceLength; i++) {
+ this._dataSourceAdded(dataSourceCollection, dataSourceCollection.get(i))
+ }
+ this._dataSourceAdded(undefined, dataSourceDisplay.defaultDataSource)
+
+ // Hook up events so that we can subscribe to future sources.
+ eventHelper.add(
+ dataSourceCollection.dataSourceAdded,
+ Viewer.prototype._dataSourceAdded,
+ this
+ )
+ eventHelper.add(
+ dataSourceCollection.dataSourceRemoved,
+ Viewer.prototype._dataSourceRemoved,
+ this
+ )
+}
+
+/**
+ *
+ */
+Object.defineProperties(Viewer.prototype, {
+ /**
+ * Gets the parent container.
+ * @memberof Viewer.prototype
+ * @type {Element}
+ * @readonly
+ */
+ container: {
+ get: function () {
+ return this._container
+ },
+ },
+
+ /**
+ * Gets the DOM element for the area at the bottom of the window containing the
+ * {@link CreditDisplay} and potentially other things.
+ * @memberof Viewer.prototype
+ * @type {Element}
+ * @readonly
+ */
+ bottomContainer: {
+ get: function () {
+ return this._bottomContainer
+ },
+ },
+
+ /**
+ * Gets the CesiumWidget.
+ * @memberof Viewer.prototype
+ * @type {CesiumWidget}
+ * @readonly
+ */
+ cesiumWidget: {
+ get: function () {
+ return this._cesiumWidget
+ },
+ },
+
+ /**
+ * Gets the display used for {@link DataSource} visualization.
+ * @memberof Viewer.prototype
+ * @type {DataSourceDisplay}
+ * @readonly
+ */
+ dataSourceDisplay: {
+ get: function () {
+ return this._dataSourceDisplay
+ },
+ },
+
+ /**
+ * Gets the collection of entities not tied to a particular data source.
+ * This is a shortcut to [dataSourceDisplay.defaultDataSource.entities]{@link Viewer#dataSourceDisplay}.
+ * @memberof Viewer.prototype
+ * @type {EntityCollection}
+ * @readonly
+ */
+ entities: {
+ get: function () {
+ return this._dataSourceDisplay.defaultDataSource.entities
+ },
+ },
+
+ /**
+ * Gets the set of {@link DataSource} instances to be visualized.
+ * @memberof Viewer.prototype
+ * @type {DataSourceCollection}
+ * @readonly
+ */
+ dataSources: {
+ get: function () {
+ return this._dataSourceCollection
+ },
+ },
+
+ /**
+ * Gets the canvas.
+ * @memberof Viewer.prototype
+ * @type {HTMLCanvasElement}
+ * @readonly
+ */
+ canvas: {
+ get: function () {
+ return this._cesiumWidget.canvas
+ },
+ },
+
+ /**
+ * Gets the scene.
+ * @memberof Viewer.prototype
+ * @type {Scene}
+ * @readonly
+ */
+ scene: {
+ get: function () {
+ return this._cesiumWidget.scene
+ },
+ },
+
+ /**
+ * Determines if shadows are cast by light sources.
+ * @memberof Viewer.prototype
+ * @type {boolean}
+ */
+ shadows: {
+ get: function () {
+ return this.scene.shadowMap.enabled
+ },
+ set: function (value) {
+ this.scene.shadowMap.enabled = value
+ },
+ },
+
+ /**
+ * Determines if the terrain casts or shadows from light sources.
+ * @memberof Viewer.prototype
+ * @type {ShadowMode}
+ */
+ terrainShadows: {
+ get: function () {
+ return this.scene.globe.shadows
+ },
+ set: function (value) {
+ this.scene.globe.shadows = value
+ },
+ },
+
+ /**
+ * Get the scene's shadow map
+ * @memberof Viewer.prototype
+ * @type {ShadowMap}
+ * @readonly
+ */
+ shadowMap: {
+ get: function () {
+ return this.scene.shadowMap
+ },
+ },
+
+ /**
+ * Gets the collection of image layers that will be rendered on the globe.
+ * @memberof Viewer.prototype
+ *
+ * @type {ImageryLayerCollection}
+ * @readonly
+ */
+ imageryLayers: {
+ get: function () {
+ return this.scene.imageryLayers
+ },
+ },
+
+ /**
+ * The terrain provider providing surface geometry for the globe.
+ * @memberof Viewer.prototype
+ *
+ * @type {TerrainProvider}
+ */
+ terrainProvider: {
+ get: function () {
+ return this.scene.terrainProvider
+ },
+ set: function (terrainProvider) {
+ this.scene.terrainProvider = terrainProvider
+ },
+ },
+
+ /**
+ * Gets the camera.
+ * @memberof Viewer.prototype
+ *
+ * @type {Camera}
+ * @readonly
+ */
+ camera: {
+ get: function () {
+ return this.scene.camera
+ },
+ },
+
+ /**
+ * Gets the post-process stages.
+ * @memberof Viewer.prototype
+ *
+ * @type {PostProcessStageCollection}
+ * @readonly
+ */
+ postProcessStages: {
+ get: function () {
+ return this.scene.postProcessStages
+ },
+ },
+
+ /**
+ * Gets the clock.
+ * @memberof Viewer.prototype
+ * @type {Clock}
+ * @readonly
+ */
+ clock: {
+ get: function () {
+ return this._clock
+ },
+ },
+
+ /**
+ * Gets the screen space event handler.
+ * @memberof Viewer.prototype
+ * @type {ScreenSpaceEventHandler}
+ * @readonly
+ */
+ screenSpaceEventHandler: {
+ get: function () {
+ return this._cesiumWidget.screenSpaceEventHandler
+ },
+ },
+
+ /**
+ * Gets or sets the target frame rate of the widget when useDefaultRenderLoop
+ * is true. If undefined, the browser's requestAnimationFrame implementation
+ * determines the frame rate. If defined, this value must be greater than 0. A value higher
+ * than the underlying requestAnimationFrame implementation will have no effect.
+ * @memberof Viewer.prototype
+ *
+ * @type {number}
+ */
+ targetFrameRate: {
+ get: function () {
+ return this._cesiumWidget.targetFrameRate
+ },
+ set: function (value) {
+ this._cesiumWidget.targetFrameRate = value
+ },
+ },
+
+ /**
+ * Gets or sets whether or not this widget should control the render loop.
+ * If true the widget will use requestAnimationFrame to
+ * perform rendering and resizing of the widget, as well as drive the
+ * simulation clock. If set to false, you must manually call the
+ * resize
, render
methods
+ * as part of a custom render loop. If an error occurs during rendering, {@link Scene}'s
+ * renderError
event will be raised and this property
+ * will be set to false. It must be set back to true to continue rendering
+ * after the error.
+ * @memberof Viewer.prototype
+ *
+ * @type {boolean}
+ */
+ useDefaultRenderLoop: {
+ get: function () {
+ return this._cesiumWidget.useDefaultRenderLoop
+ },
+ set: function (value) {
+ this._cesiumWidget.useDefaultRenderLoop = value
+ },
+ },
+
+ /**
+ * Gets or sets a scaling factor for rendering resolution. Values less than 1.0 can improve
+ * performance on less powerful devices while values greater than 1.0 will render at a higher
+ * resolution and then scale down, resulting in improved visual fidelity.
+ * For example, if the widget is laid out at a size of 640x480, setting this value to 0.5
+ * will cause the scene to be rendered at 320x240 and then scaled up while setting
+ * it to 2.0 will cause the scene to be rendered at 1280x960 and then scaled down.
+ * @memberof Viewer.prototype
+ *
+ * @type {number}
+ * @default 1.0
+ */
+ resolutionScale: {
+ get: function () {
+ return this._cesiumWidget.resolutionScale
+ },
+ set: function (value) {
+ this._cesiumWidget.resolutionScale = value
+ },
+ },
+
+ /**
+ * Boolean flag indicating if the browser's recommended resolution is used.
+ * If true, the browser's device pixel ratio is ignored and 1.0 is used instead,
+ * effectively rendering based on CSS pixels instead of device pixels. This can improve
+ * performance on less powerful devices that have high pixel density. When false, rendering
+ * will be in device pixels. {@link Viewer#resolutionScale} will still take effect whether
+ * this flag is true or false.
+ * @memberof Viewer.prototype
+ *
+ * @type {boolean}
+ * @default true
+ */
+ useBrowserRecommendedResolution: {
+ get: function () {
+ return this._cesiumWidget.useBrowserRecommendedResolution
+ },
+ set: function (value) {
+ this._cesiumWidget.useBrowserRecommendedResolution = value
+ },
+ },
+
+ /**
+ * Gets or sets whether or not data sources can temporarily pause
+ * animation in order to avoid showing an incomplete picture to the user.
+ * For example, if asynchronous primitives are being processed in the
+ * background, the clock will not advance until the geometry is ready.
+ *
+ * @memberof Viewer.prototype
+ *
+ * @type {boolean}
+ */
+ allowDataSourcesToSuspendAnimation: {
+ get: function () {
+ return this._allowDataSourcesToSuspendAnimation
+ },
+ set: function (value) {
+ this._allowDataSourcesToSuspendAnimation = value
+ },
+ },
+
+ /**
+ * Gets or sets the Entity instance currently being tracked by the camera.
+ * @memberof Viewer.prototype
+ * @type {Entity | undefined}
+ */
+ trackedEntity: {
+ get: function () {
+ return this._trackedEntity
+ },
+ set: function (value) {
+ if (this._trackedEntity !== value) {
+ this._trackedEntity = value
+
+ //Cancel any pending zoom
+ cancelZoom(this)
+
+ const scene = this.scene
+ const sceneMode = scene.mode
+
+ //Stop tracking
+ if (!defined(value) || !defined(value.position)) {
+ this._needTrackedEntityUpdate = false
+ if (
+ sceneMode === SceneMode.COLUMBUS_VIEW ||
+ sceneMode === SceneMode.SCENE2D
+ ) {
+ scene.screenSpaceCameraController.enableTranslate = true
+ }
+
+ if (
+ sceneMode === SceneMode.COLUMBUS_VIEW ||
+ sceneMode === SceneMode.SCENE3D
+ ) {
+ scene.screenSpaceCameraController.enableTilt = true
+ }
+
+ this._entityView = undefined
+ this.camera.lookAtTransform(Matrix4.IDENTITY)
+ } else {
+ //We can't start tracking immediately, so we set a flag and start tracking
+ //when the bounding sphere is ready (most likely next frame).
+ this._needTrackedEntityUpdate = true
+ }
+
+ this._trackedEntityChanged.raiseEvent(value)
+ this.scene.requestRender()
+ }
+ },
+ },
+ /**
+ * Gets or sets the object instance for which to display a selection indicator.
+ *
+ * If a user interactively picks a Cesium3DTilesFeature instance, then this property
+ * will contain a transient Entity instance with a property named "feature" that is
+ * the instance that was picked.
+ * @memberof Viewer.prototype
+ * @type {Entity | undefined}
+ */
+ selectedEntity: {
+ get: function () {
+ return this._selectedEntity
+ },
+ set: function (value) {
+ if (this._selectedEntity !== value) {
+ this._selectedEntity = value
+ this._selectedEntityChanged.raiseEvent(value)
+ }
+ },
+ },
+ /**
+ * Gets the event that is raised when the selected entity changes.
+ * @memberof Viewer.prototype
+ * @type {Event}
+ * @readonly
+ */
+ selectedEntityChanged: {
+ get: function () {
+ return this._selectedEntityChanged
+ },
+ },
+ /**
+ * Gets the event that is raised when the tracked entity changes.
+ * @memberof Viewer.prototype
+ * @type {Event}
+ * @readonly
+ */
+ trackedEntityChanged: {
+ get: function () {
+ return this._trackedEntityChanged
+ },
+ },
+ /**
+ * Gets or sets the data source to track with the viewer's clock.
+ * @memberof Viewer.prototype
+ * @type {DataSource}
+ */
+ clockTrackedDataSource: {
+ get: function () {
+ return this._clockTrackedDataSource
+ },
+ set: function (value) {
+ if (this._clockTrackedDataSource !== value) {
+ this._clockTrackedDataSource = value
+ trackDataSourceClock(undefined, this.clock, value)
+ }
+ },
+ },
+})
+
+/**
+ * Extends the base viewer functionality with the provided mixin.
+ * A mixin may add additional properties, functions, or other behavior
+ * to the provided viewer instance.
+ *
+ * @param {Viewer.ViewerMixin} mixin The Viewer mixin to add to this instance.
+ * @param {object} [options] The options object to be passed to the mixin function.
+ *
+ * @see viewerDragDropMixin
+ */
+Viewer.prototype.extend = function (mixin, options) {
+ //>>includeStart('debug', pragmas.debug);
+ if (!defined(mixin)) {
+ throw new DeveloperError('mixin is required.')
+ }
+ //>>includeEnd('debug')
+
+ mixin(this, options)
+}
+
+/**
+ * Resizes the widget to match the container size.
+ * This function is called automatically as needed unless
+ * useDefaultRenderLoop
is set to false.
+ */
+Viewer.prototype.resize = function () {
+ const cesiumWidget = this._cesiumWidget
+ const container = this._container
+ const width = container.clientWidth
+ const height = container.clientHeight
+ cesiumWidget.resize()
+ if (width === this._lastWidth && height === this._lastHeight) {
+ return
+ }
+ let creditLeft = 0
+ let creditBottom = 0
+ this._bottomContainer.style.left = `${creditLeft}px`
+ this._bottomContainer.style.bottom = `${creditBottom}px`
+ this._lastWidth = width
+ this._lastHeight = height
+}
+
+/**
+ * This forces the widget to re-think its layout, including
+ * widget sizes and credit placement.
+ */
+Viewer.prototype.forceResize = function () {
+ this._lastWidth = 0
+ this.resize()
+}
+
+/**
+ * Renders the scene. This function is called automatically
+ * unless useDefaultRenderLoop
is set to false;
+ */
+Viewer.prototype.render = function () {
+ this._cesiumWidget.render()
+}
+
+/**
+ * @returns {Boolean} true if the object has been destroyed, false otherwise.
+ */
+Viewer.prototype.isDestroyed = function () {
+ return false
+}
+
+/**
+ * Destroys the widget. Should be called if permanently
+ * removing the widget from layout.
+ */
+Viewer.prototype.destroy = function () {
+ let i
+
+ // Unsubscribe from data sources
+ const dataSources = this.dataSources
+ const dataSourceLength = dataSources.length
+ for (i = 0; i < dataSourceLength; i++) {
+ this._dataSourceRemoved(dataSources, dataSources.get(i))
+ }
+ this._dataSourceRemoved(undefined, this._dataSourceDisplay.defaultDataSource)
+
+ this._eventHelper.removeAll()
+
+ this._dataSourceDisplay = this._dataSourceDisplay.destroy()
+ this._cesiumWidget = this._cesiumWidget.destroy()
+
+ if (this._destroyDataSourceCollection) {
+ this._dataSourceCollection = this._dataSourceCollection.destroy()
+ }
+
+ return destroyObject(this)
+}
+
+/**
+ * @private
+ */
+Viewer.prototype._dataSourceAdded = function (
+ dataSourceCollection,
+ dataSource
+) {
+ const entityCollection = dataSource.entities
+ entityCollection.collectionChanged.addEventListener(
+ Viewer.prototype._onEntityCollectionChanged,
+ this
+ )
+}
+
+/**
+ * @private
+ */
+Viewer.prototype._dataSourceRemoved = function (
+ dataSourceCollection,
+ dataSource
+) {
+ const entityCollection = dataSource.entities
+ entityCollection.collectionChanged.removeEventListener(
+ Viewer.prototype._onEntityCollectionChanged,
+ this
+ )
+
+ if (defined(this.trackedEntity)) {
+ if (
+ entityCollection.getById(this.trackedEntity.id) === this.trackedEntity
+ ) {
+ this.trackedEntity = undefined
+ }
+ }
+
+ if (defined(this.selectedEntity)) {
+ if (
+ entityCollection.getById(this.selectedEntity.id) === this.selectedEntity
+ ) {
+ this.selectedEntity = undefined
+ }
+ }
+}
+
+/**
+ * @private
+ */
+Viewer.prototype._onTick = function (clock) {
+ const time = clock.currentTime
+
+ const isUpdated = this._dataSourceDisplay.update(time)
+ if (this._allowDataSourcesToSuspendAnimation) {
+ this._clock.canAnimate = isUpdated
+ }
+
+ const entityView = this._entityView
+ if (defined(entityView)) {
+ const trackedEntity = this._trackedEntity
+ const trackedState = this._dataSourceDisplay.getBoundingSphere(
+ trackedEntity,
+ false,
+ boundingSphereScratch
+ )
+ if (trackedState === BoundingSphereState.DONE) {
+ entityView.update(time, boundingSphereScratch)
+ }
+ }
+
+ let position
+ let enableCamera = false
+ const selectedEntity = this.selectedEntity
+ const showSelection = defined(selectedEntity) && this._enableInfoOrSelection
+
+ if (
+ showSelection &&
+ selectedEntity.isShowing &&
+ selectedEntity.isAvailable(time)
+ ) {
+ const state = this._dataSourceDisplay.getBoundingSphere(
+ selectedEntity,
+ true,
+ boundingSphereScratch
+ )
+ if (state !== BoundingSphereState.FAILED) {
+ position = boundingSphereScratch.center
+ } else if (defined(selectedEntity.position)) {
+ position = selectedEntity.position.getValue(time, position)
+ }
+ enableCamera = defined(position)
+ }
+}
+
+/**
+ * @private
+ */
+Viewer.prototype._onEntityCollectionChanged = function (
+ collection,
+ added,
+ removed
+) {
+ const length = removed.length
+ for (let i = 0; i < length; i++) {
+ const removedObject = removed[i]
+ if (this.trackedEntity === removedObject) {
+ this.trackedEntity = undefined
+ }
+ if (this.selectedEntity === removedObject) {
+ this.selectedEntity = undefined
+ }
+ }
+}
+
+/**
+ * @private
+ */
+Viewer.prototype._onInfoBoxCameraClicked = function (infoBoxViewModel) {
+ if (
+ infoBoxViewModel.isCameraTracking &&
+ this.trackedEntity === this.selectedEntity
+ ) {
+ this.trackedEntity = undefined
+ } else {
+ const selectedEntity = this.selectedEntity
+ const position = selectedEntity.position
+ if (defined(position)) {
+ this.trackedEntity = this.selectedEntity
+ } else {
+ this.zoomTo(this.selectedEntity)
+ }
+ }
+}
+
+/**
+ * @private
+ */
+Viewer.prototype._clearTrackedObject = function () {
+ this.trackedEntity = undefined
+}
+
+/**
+ * @private
+ */
+Viewer.prototype._clearObjects = function () {
+ this.trackedEntity = undefined
+ this.selectedEntity = undefined
+}
+
+/**
+ * @private
+ */
+Viewer.prototype._onDataSourceChanged = function (dataSource) {
+ if (this.clockTrackedDataSource === dataSource) {
+ trackDataSourceClock(undefined, this.clock, dataSource)
+ }
+}
+
+/**
+ * @private
+ */
+Viewer.prototype._onDataSourceAdded = function (
+ dataSourceCollection,
+ dataSource
+) {
+ if (this._automaticallyTrackDataSourceClocks) {
+ this.clockTrackedDataSource = dataSource
+ }
+ const id = dataSource.entities.id
+ const removalFunc = this._eventHelper.add(
+ dataSource.changedEvent,
+ Viewer.prototype._onDataSourceChanged,
+ this
+ )
+ this._dataSourceChangedListeners[id] = removalFunc
+}
+
+/**
+ * @private
+ */
+Viewer.prototype._onDataSourceRemoved = function (
+ dataSourceCollection,
+ dataSource
+) {
+ const resetClock = this.clockTrackedDataSource === dataSource
+ const id = dataSource.entities.id
+ this._dataSourceChangedListeners[id]()
+ this._dataSourceChangedListeners[id] = undefined
+ if (resetClock) {
+ const numDataSources = dataSourceCollection.length
+ if (this._automaticallyTrackDataSourceClocks && numDataSources > 0) {
+ this.clockTrackedDataSource = dataSourceCollection.get(numDataSources - 1)
+ } else {
+ this.clockTrackedDataSource = undefined
+ }
+ }
+}
+
+/**
+ * Asynchronously sets the camera to view the provided entity, entities, or data source.
+ * If the data source is still in the process of loading or the visualization is otherwise still loading,
+ * this method waits for the data to be ready before performing the zoom.
+ *
+ * The offset is heading/pitch/range in the local east-north-up reference frame centered at the center of the bounding sphere.
+ * The heading and the pitch angles are defined in the local east-north-up reference frame.
+ * The heading is the angle from y axis and increasing towards the x axis. Pitch is the rotation from the xy-plane. Positive pitch
+ * angles are above the plane. Negative pitch angles are below the plane. The range is the distance from the center. If the range is
+ * zero, a range will be computed such that the whole bounding sphere is visible.
+ *
+ * In 2D, there must be a top down view. The camera will be placed above the target looking down. The height above the
+ * target will be the range. The heading will be determined from the offset. If the heading cannot be
+ * determined from the offset, the heading will be north.
+ *
+ * @param {Entity|Entity[]|EntityCollection|DataSource|ImageryLayer|Cesium3DTileset|TimeDynamicPointCloud|Promise.} target The entity, array of entities, entity collection, data source, Cesium3DTileset, point cloud, or imagery layer to view. You can also pass a promise that resolves to one of the previously mentioned types.
+ * @param {HeadingPitchRange} [offset] The offset from the center of the entity in the local east-north-up reference frame.
+ * @returns {Promise.} A Promise that resolves to true if the zoom was successful or false if the target is not currently visualized in the scene or the zoom was cancelled.
+ */
+Viewer.prototype.zoomTo = function (target, offset) {
+ const options = {
+ offset: offset,
+ }
+ return zoomToOrFly(this, target, options, false)
+}
+
+/**
+ * Flies the camera to the provided entity, entities, or data source.
+ * If the data source is still in the process of loading or the visualization is otherwise still loading,
+ * this method waits for the data to be ready before performing the flight.
+ *
+ * The offset is heading/pitch/range in the local east-north-up reference frame centered at the center of the bounding sphere.
+ * The heading and the pitch angles are defined in the local east-north-up reference frame.
+ * The heading is the angle from y axis and increasing towards the x axis. Pitch is the rotation from the xy-plane. Positive pitch
+ * angles are above the plane. Negative pitch angles are below the plane. The range is the distance from the center. If the range is
+ * zero, a range will be computed such that the whole bounding sphere is visible.
+ *
+ * In 2D, there must be a top down view. The camera will be placed above the target looking down. The height above the
+ * target will be the range. The heading will be determined from the offset. If the heading cannot be
+ * determined from the offset, the heading will be north.
+ *
+ * @param {Entity|Entity[]|EntityCollection|DataSource|ImageryLayer|Cesium3DTileset|TimeDynamicPointCloud|Promise} target The entity, array of entities, entity collection, data source, Cesium3DTileset, point cloud, or imagery layer to view. You can also pass a promise that resolves to one of the previously mentioned types.
+ * @param {object} [options] Object with the following properties:
+ * @param {number} [options.duration=3.0] The duration of the flight in seconds.
+ * @param {number} [options.maximumHeight] The maximum height at the peak of the flight.
+ * @param {HeadingPitchRange} [options.offset] The offset from the target in the local east-north-up reference frame centered at the target.
+ * @returns {Promise} A Promise that resolves to true if the flight was successful or false if the target is not currently visualized in the scene or the flight was cancelled. //TODO: Cleanup entity mentions
+ */
+Viewer.prototype.flyTo = function (target, options) {
+ return zoomToOrFly(this, target, options, true)
+}
+
+function zoomToOrFly(that, zoomTarget, options, isFlight) {
+ //>>includeStart('debug', pragmas.debug);
+ if (!defined(zoomTarget)) {
+ throw new DeveloperError('zoomTarget is required.')
+ }
+ //>>includeEnd('debug');
+
+ cancelZoom(that)
+
+ //We can't actually perform the zoom until all visualization is ready and
+ //bounding spheres have been computed. Therefore we create and return
+ //a deferred which will be resolved as part of the post-render step in the
+ //frame that actually performs the zoom.
+ const zoomPromise = new Promise((resolve) => {
+ that._completeZoom = function (value) {
+ resolve(value)
+ }
+ })
+ that._zoomPromise = zoomPromise
+ that._zoomIsFlight = isFlight
+ that._zoomOptions = options
+
+ Promise.resolve(zoomTarget).then(function (zoomTarget) {
+ //Only perform the zoom if it wasn't cancelled before the promise resolved.
+ if (that._zoomPromise !== zoomPromise) {
+ return
+ }
+
+ //If the zoom target is a rectangular imagery in an ImageLayer
+ if (zoomTarget instanceof ImageryLayer) {
+ let rectanglePromise
+
+ if (defined(zoomTarget.imageryProvider)) {
+ // This is here for backward compatibility. It can be removed when readyPromise is removed.
+ rectanglePromise = zoomTarget.imageryProvider._readyPromise.then(() => {
+ return zoomTarget.getImageryRectangle()
+ })
+ } else {
+ rectanglePromise = new Promise((resolve) => {
+ const removeListener = zoomTarget.readyEvent.addEventListener(() => {
+ removeListener()
+ resolve(zoomTarget.getImageryRectangle())
+ })
+ })
+ }
+ rectanglePromise
+ .then(function (rectangle) {
+ return computeFlyToLocationForRectangle(rectangle, that.scene)
+ })
+ .then(function (position) {
+ //Only perform the zoom if it wasn't cancelled before the promise was resolved
+ if (that._zoomPromise === zoomPromise) {
+ that._zoomTarget = position
+ }
+ })
+ return
+ }
+
+ if (
+ zoomTarget instanceof Cesium3DTileset ||
+ zoomTarget instanceof TimeDynamicPointCloud ||
+ zoomTarget instanceof VoxelPrimitive
+ ) {
+ that._zoomTarget = zoomTarget
+ return
+ }
+
+ //If the zoom target is a data source, and it's in the middle of loading, wait for it to finish loading.
+ if (zoomTarget.isLoading && defined(zoomTarget.loadingEvent)) {
+ const removeEvent = zoomTarget.loadingEvent.addEventListener(function () {
+ removeEvent()
+
+ //Only perform the zoom if it wasn't cancelled before the data source finished.
+ if (that._zoomPromise === zoomPromise) {
+ that._zoomTarget = zoomTarget.entities.values.slice(0)
+ }
+ })
+ return
+ }
+
+ //Zoom target is already an array, just copy it and return.
+ if (Array.isArray(zoomTarget)) {
+ that._zoomTarget = zoomTarget.slice(0)
+ return
+ }
+
+ //If zoomTarget is an EntityCollection, this will retrieve the array
+ zoomTarget = defaultValue(zoomTarget.values, zoomTarget)
+
+ //If zoomTarget is a DataSource, this will retrieve the array.
+ if (defined(zoomTarget.entities)) {
+ zoomTarget = zoomTarget.entities.values
+ }
+
+ //Zoom target is already an array, just copy it and return.
+ if (Array.isArray(zoomTarget)) {
+ that._zoomTarget = zoomTarget.slice(0)
+ } else {
+ //Single entity
+ that._zoomTarget = [zoomTarget]
+ }
+ })
+
+ that.scene.requestRender()
+ return zoomPromise
+}
+
+function clearZoom(viewer) {
+ viewer._zoomPromise = undefined
+ viewer._zoomTarget = undefined
+ viewer._zoomOptions = undefined
+}
+
+function cancelZoom(viewer) {
+ const zoomPromise = viewer._zoomPromise
+ if (defined(zoomPromise)) {
+ clearZoom(viewer)
+ viewer._completeZoom(false)
+ }
+}
+
+/**
+ * @private
+ */
+Viewer.prototype._postRender = function () {
+ updateZoomTarget(this)
+ updateTrackedEntity(this)
+}
+function updateZoomTarget(viewer) {
+ const target = viewer._zoomTarget
+ if (!defined(target) || viewer.scene.mode === SceneMode.MORPHING) {
+ return
+ }
+
+ const scene = viewer.scene
+ const camera = scene.camera
+ const zoomOptions = defaultValue(viewer._zoomOptions, {})
+ let options
+
+ // If zoomTarget was Cesium3DTileset
+ if (target instanceof Cesium3DTileset || target instanceof VoxelPrimitive) {
+ // This is here for backwards compatibility and can be removed once Cesium3DTileset.readyPromise and VoxelPrimitive.readyPromise is removed.
+ return target._readyPromise
+ .then(function () {
+ const boundingSphere = target.boundingSphere
+ // If offset was originally undefined then give it base value instead of empty object
+ if (!defined(zoomOptions.offset)) {
+ zoomOptions.offset = new HeadingPitchRange(
+ 0.0,
+ -0.5,
+ boundingSphere.radius
+ )
+ }
+
+ options = {
+ offset: zoomOptions.offset,
+ duration: zoomOptions.duration,
+ maximumHeight: zoomOptions.maximumHeight,
+ complete: function () {
+ viewer._completeZoom(true)
+ },
+ cancel: function () {
+ viewer._completeZoom(false)
+ },
+ }
+
+ if (viewer._zoomIsFlight) {
+ camera.flyToBoundingSphere(target.boundingSphere, options)
+ } else {
+ camera.viewBoundingSphere(boundingSphere, zoomOptions.offset)
+ camera.lookAtTransform(Matrix4.IDENTITY)
+
+ // Finish the promise
+ viewer._completeZoom(true)
+ }
+
+ clearZoom(viewer)
+ })
+ .catch(() => {
+ cancelZoom(viewer)
+ })
+ }
+
+ // If zoomTarget was TimeDynamicPointCloud
+ if (target instanceof TimeDynamicPointCloud) {
+ // This is here for backwards compatibility and can be removed once TimeDynamicPointCloud.readyPromise is removed.
+ return target._readyPromise.then(function () {
+ const boundingSphere = target.boundingSphere
+ // If offset was originally undefined then give it base value instead of empty object
+ if (!defined(zoomOptions.offset)) {
+ zoomOptions.offset = new HeadingPitchRange(
+ 0.0,
+ -0.5,
+ boundingSphere.radius
+ )
+ }
+
+ options = {
+ offset: zoomOptions.offset,
+ duration: zoomOptions.duration,
+ maximumHeight: zoomOptions.maximumHeight,
+ complete: function () {
+ viewer._completeZoom(true)
+ },
+ cancel: function () {
+ viewer._completeZoom(false)
+ },
+ }
+
+ if (viewer._zoomIsFlight) {
+ camera.flyToBoundingSphere(boundingSphere, options)
+ } else {
+ camera.viewBoundingSphere(boundingSphere, zoomOptions.offset)
+ camera.lookAtTransform(Matrix4.IDENTITY)
+
+ // Finish the promise
+ viewer._completeZoom(true)
+ }
+
+ clearZoom(viewer)
+ })
+ }
+
+ // If zoomTarget was an ImageryLayer
+ if (target instanceof Cartographic) {
+ options = {
+ destination:
+ scene.mapProjection.ellipsoid.cartographicToCartesian(target),
+ duration: zoomOptions.duration,
+ maximumHeight: zoomOptions.maximumHeight,
+ complete: function () {
+ viewer._completeZoom(true)
+ },
+ cancel: function () {
+ viewer._completeZoom(false)
+ },
+ }
+
+ if (viewer._zoomIsFlight) {
+ camera.flyTo(options)
+ } else {
+ camera.setView(options)
+ viewer._completeZoom(true)
+ }
+ clearZoom(viewer)
+ return
+ }
+
+ const entities = target
+
+ const boundingSpheres = []
+ for (let i = 0, len = entities.length; i < len; i++) {
+ const state = viewer._dataSourceDisplay.getBoundingSphere(
+ entities[i],
+ false,
+ boundingSphereScratch
+ )
+
+ if (state === BoundingSphereState.PENDING) {
+ return
+ } else if (state !== BoundingSphereState.FAILED) {
+ boundingSpheres.push(BoundingSphere.clone(boundingSphereScratch))
+ }
+ }
+
+ if (boundingSpheres.length === 0) {
+ cancelZoom(viewer)
+ return
+ }
+
+ //Stop tracking the current entity.
+ viewer.trackedEntity = undefined
+
+ const boundingSphere = BoundingSphere.fromBoundingSpheres(boundingSpheres)
+
+ if (!viewer._zoomIsFlight) {
+ camera.viewBoundingSphere(boundingSphere, zoomOptions.offset)
+ camera.lookAtTransform(Matrix4.IDENTITY)
+ clearZoom(viewer)
+ viewer._completeZoom(true)
+ } else {
+ clearZoom(viewer)
+ camera.flyToBoundingSphere(boundingSphere, {
+ duration: zoomOptions.duration,
+ maximumHeight: zoomOptions.maximumHeight,
+ complete: function () {
+ viewer._completeZoom(true)
+ },
+ cancel: function () {
+ viewer._completeZoom(false)
+ },
+ offset: zoomOptions.offset,
+ })
+ }
+}
+
+function updateTrackedEntity(viewer) {
+ if (!viewer._needTrackedEntityUpdate) {
+ return
+ }
+
+ const trackedEntity = viewer._trackedEntity
+ const currentTime = viewer.clock.currentTime
+
+ //Verify we have a current position at this time. This is only triggered if a position
+ //has become undefined after trackedEntity is set but before the boundingSphere has been
+ //computed. In this case, we will track the entity once it comes back into existence.
+ const currentPosition = Property.getValueOrUndefined(
+ trackedEntity.position,
+ currentTime
+ )
+
+ if (!defined(currentPosition)) {
+ return
+ }
+
+ const scene = viewer.scene
+
+ const state = viewer._dataSourceDisplay.getBoundingSphere(
+ trackedEntity,
+ false,
+ boundingSphereScratch
+ )
+ if (state === BoundingSphereState.PENDING) {
+ return
+ }
+
+ const sceneMode = scene.mode
+ if (
+ sceneMode === SceneMode.COLUMBUS_VIEW ||
+ sceneMode === SceneMode.SCENE2D
+ ) {
+ scene.screenSpaceCameraController.enableTranslate = false
+ }
+
+ if (
+ sceneMode === SceneMode.COLUMBUS_VIEW ||
+ sceneMode === SceneMode.SCENE3D
+ ) {
+ scene.screenSpaceCameraController.enableTilt = false
+ }
+
+ const bs =
+ state !== BoundingSphereState.FAILED ? boundingSphereScratch : undefined
+ viewer._entityView = new EntityView(
+ trackedEntity,
+ scene,
+ scene.mapProjection.ellipsoid
+ )
+ viewer._entityView.update(currentTime, bs)
+ viewer._needTrackedEntityUpdate = false
+}
+
+/**
+ * A function that augments a Viewer instance with additional functionality.
+ * @callback Viewer.ViewerMixin
+ * @param {Viewer} viewer The viewer instance.
+ * @param {object} options Options object to be passed to the mixin function.
+ *
+ * @see Viewer#extend
+ */
+export default Viewer
diff --git a/src/modules/exts/index.js b/src/modules/exts/index.js
new file mode 100644
index 00000000..4ed63e68
--- /dev/null
+++ b/src/modules/exts/index.js
@@ -0,0 +1,8 @@
+/**
+ @author : Caven Chen
+ @date : 2023-05-09
+ */
+
+export { default as BaseLayerPicker } from './BaseLayerPicker.js'
+export { default as GroundSkyBox } from './GroundSkyBox.js'
+export { default as CesiumViewer } from './Viewer.js'
diff --git a/src/modules/history-track/Track.js b/src/modules/history-track/Track.js
new file mode 100644
index 00000000..3beaaa73
--- /dev/null
+++ b/src/modules/history-track/Track.js
@@ -0,0 +1,426 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-19 11:21:48
+ */
+
+import { Cesium } from '../../namespace'
+import { TrackEvent, TrackEventType } from '../event'
+import State from '../state/State'
+import Parse from '../parse/Parse'
+import { Util } from '../utils'
+import { Transform } from '../transform'
+import { heading, distance } from '../math'
+import TrackViewMode from './TrackViewMode'
+
+const DEF_OPTS = {
+ clampToGround: false,
+ clampToTileset: false,
+ interpolationType: 'Linear',
+ interpolationDegree: 2,
+ endDelayTime: 0.5,
+ headingOffset: 0,
+}
+
+const DEF_PATH_STYLE = {
+ width: 2,
+ material: Cesium.Color.ORANGE,
+ clampToGround: true,
+ depthFailMaterial: Cesium.Color.ORANGE.withAlpha(0.8),
+}
+
+class Track {
+ constructor(positions, duration, callback, options) {
+ this._id = Util.uuid()
+ this._bid = undefined
+ this._positions = Parse.parsePositions(positions)
+ this._duration = duration || 20
+ this._callback = callback
+ this._options = {
+ ...DEF_OPTS,
+ ...options,
+ }
+ this._controller = undefined
+ this._sampledPosition = undefined
+ this._velocityOrientation = undefined
+ this._viewed = false
+ this._delegate = new Cesium.Entity()
+ this._pathPositions = []
+ this._path = new Cesium.Entity({
+ show: false,
+ polyline: {
+ positions: new Cesium.CallbackProperty(() => {
+ return this._pathPositions
+ }, false),
+ },
+ })
+ this._positionIndex = 0
+ this._timeLine = []
+ this._startTime = undefined
+ this._endTime = undefined
+ this._trackEvent = new TrackEvent()
+ this._trackEvent.on(TrackEventType.POST_RENDER, this._onPostRender, this)
+ this._trackEvent.on(TrackEventType.ADD, this._onAdd, this)
+ this._trackEvent.on(TrackEventType.REMOVE, this._onRemove, this)
+ this._trackEvent.on(
+ TrackEventType.RESET_TIME_LINE,
+ this._resetTimeLine,
+ this
+ )
+ this._state = State.INITIALIZED
+ }
+
+ get trackId() {
+ return this._id
+ }
+
+ set id(id) {
+ this._bid = id
+ }
+
+ get id() {
+ return this._bid
+ }
+
+ set positions(positions) {
+ this._positions = Parse.parsePositions(positions)
+ this._resetTimeLine({})
+ }
+
+ get positions() {
+ return this._positions
+ }
+
+ set duration(duration) {
+ this._duration = duration
+ this._resetTimeLine({})
+ }
+
+ get duration() {
+ return this._duration
+ }
+
+ set startTime(startTime) {
+ if (startTime instanceof Date) {
+ this._startTime = Cesium.JulianDate.fromDate(startTime)
+ } else {
+ this._startTime = startTime
+ }
+ this._resetTimeLine({})
+ }
+
+ get startTime() {
+ return this._startTime
+ }
+
+ set viewed(viewed) {
+ this._viewed = viewed
+ }
+
+ get viewed() {
+ return this._viewed
+ }
+
+ get trackEvent() {
+ return this._trackEvent
+ }
+
+ get state() {
+ return this._state
+ }
+
+ /**
+ * add to entities
+ * @param controller
+ * @private
+ */
+ _onAdd(controller) {
+ if (!controller) {
+ return false
+ }
+ this._controller = controller
+ this._controller.delegate.add(this._delegate)
+ this._controller.delegate.add(this._path)
+ !this._startTime && (this._startTime = Cesium.JulianDate.now())
+ this._state = State.ADDED
+ }
+
+ /**
+ * remove from entities
+ * @private
+ */
+ _onRemove() {
+ if (!this._controller) {
+ return false
+ }
+ this._controller.delegate.remove(this._delegate)
+ this._controller.delegate.remove(this._path)
+ this._viewed = false
+ this._startTime = undefined
+ this._state = State.REMOVED
+ }
+
+ /**
+ *
+ * @param viewer
+ * @param viewOption
+ * @private
+ */
+ _onPostRender({ viewer, viewOption }) {
+ if (!this._startTime || !this._endTime) {
+ return false
+ }
+ let now = Cesium.JulianDate.now()
+ if (Cesium.JulianDate.lessThanOrEquals(now, this._endTime)) {
+ let p = this._sampledPosition.getValue(now)
+ if (!p) {
+ return false
+ }
+ this._pathPositions.push(p)
+ if (this._options.clampToTileset) {
+ this._delegate.position = viewer.scene.clampToHeight(p, [
+ this._delegate,
+ ])
+ } else {
+ this._delegate.position = p
+ }
+ let orientation = this._velocityOrientation.getValue(now)
+ if (orientation) {
+ let quaternion = Cesium.Quaternion.fromHeadingPitchRoll(
+ new Cesium.HeadingPitchRoll(
+ Cesium.Math.toRadians(this._options.headingOffset || 0),
+ 0,
+ 0
+ ),
+ new Cesium.Quaternion()
+ )
+ this._delegate.orientation = Cesium.Quaternion.multiply(
+ orientation,
+ quaternion,
+ new Cesium.Quaternion()
+ )
+ }
+ let time = this._timeLine[this._positionIndex]
+ if (time) {
+ let timeDiff = Cesium.JulianDate.secondsDifference(now, time)
+ if (timeDiff >= 0 && timeDiff <= 1) {
+ let position = this._positions[this._positionIndex] || undefined
+ if (position && orientation) {
+ let mat = Cesium.Matrix3.fromQuaternion(orientation)
+ let mat4 = Cesium.Matrix4.fromRotationTranslation(mat, p)
+ let hpr = Cesium.Transforms.fixedFrameToHeadingPitchRoll(mat4)
+ position.heading = Cesium.Math.toDegrees(hpr.heading)
+ position.pitch = Cesium.Math.toDegrees(hpr.pitch)
+ position.roll = Cesium.Math.toDegrees(hpr.roll)
+ }
+ this._callback &&
+ this._callback(
+ position,
+ this._positionIndex + 1 === this._positions.length
+ )
+ this._positionIndex++
+ }
+ }
+ }
+ this._setCameraView(viewer, viewOption)
+ }
+
+ /**
+ * Sets camera position
+ * @param viewer
+ * @param viewOption
+ * @private
+ */
+ _setCameraView(viewer, viewOption) {
+ if (!this._viewed) {
+ return false
+ }
+ let now = Cesium.JulianDate.now()
+ if (Cesium.JulianDate.greaterThan(now, this._endTime)) {
+ viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)
+ viewer.delegate.trackedEntity &&
+ (viewer.delegate.trackedEntity = undefined)
+ this._viewed = false
+ } else {
+ let p = this._sampledPosition.getValue(now)
+ let next_p = this._sampledPosition.getValue(
+ Cesium.JulianDate.addSeconds(now, 1 / 60, new Cesium.JulianDate())
+ )
+ if (p && next_p) {
+ if (
+ viewOption?.mode === TrackViewMode.TRACKED &&
+ viewer.delegate?.trackedEntity?.id !== this._delegate?.id
+ ) {
+ viewer.delegate.trackedEntity = this._delegate
+ } else if (viewOption?.mode === TrackViewMode.FP) {
+ viewer.camera.lookAt(
+ p,
+ new Cesium.HeadingPitchRange(
+ heading(p, next_p),
+ Cesium.Math.toRadians(viewOption?.pitch || 0),
+ viewOption?.range || 10
+ )
+ )
+ } else if (viewOption?.mode === TrackViewMode.TP) {
+ viewer.camera.lookAt(
+ p,
+ new Cesium.HeadingPitchRange(
+ 0,
+ Cesium.Math.toRadians(viewOption?.pitch || -90),
+ viewOption?.range || 1000
+ )
+ )
+ }
+ }
+ }
+ }
+
+ /**
+ *
+ * @param params
+ * @returns {boolean}
+ * @private
+ */
+ _resetTimeLine(params) {
+ if (!this._startTime || !this._duration || !this._positions?.length) {
+ return false
+ }
+ let interval = 0
+ if (!params?.stopTime && !params?.duration) {
+ let v = distance(this._positions) / this._duration
+ this._timeLine = this._positions.map((item, index, arr) => {
+ if (index !== 0) {
+ interval += distance([arr[index - 1], item]) / v
+ }
+ return Cesium.JulianDate.addSeconds(
+ this._startTime,
+ interval,
+ new Cesium.JulianDate()
+ )
+ })
+ this._pathPositions = []
+ this._positionIndex = 0
+ } else if (params?.stopTime && params?.duration) {
+ this._duration += params.duration
+ this._timeLine = this._timeLine.map((item) => {
+ if (Cesium.JulianDate.greaterThan(item, params.stopTime)) {
+ item = Cesium.JulianDate.addSeconds(
+ item,
+ params.duration,
+ new Cesium.JulianDate()
+ )
+ }
+ return item
+ })
+ }
+ this._sampledPosition = new Cesium.SampledPositionProperty()
+ this._sampledPosition.addSamples(
+ this._timeLine,
+ Transform.transformWGS84ArrayToCartesianArray(this._positions)
+ )
+ this._sampledPosition.forwardExtrapolationType =
+ Cesium.ExtrapolationType.HOLD
+ /// setInterpolationOptions
+ if (this._options.interpolationType === 'Hermite') {
+ this._sampledPosition.setInterpolationOptions({
+ interpolationDegree: this._options.interpolationDegree || 2,
+ interpolationAlgorithm: Cesium.HermitePolynomialApproximation,
+ })
+ } else if (this._options.interpolationType === 'Linear') {
+ this._sampledPosition.setInterpolationOptions({
+ interpolationDegree: this._options.interpolationDegree || 1,
+ interpolationAlgorithm: Cesium.LinearApproximation,
+ })
+ } else if (this._options.interpolationType === 'Lagrange') {
+ this._sampledPosition.setInterpolationOptions({
+ interpolationDegree: this._options.interpolationDegree || 5,
+ interpolationAlgorithm: Cesium.LagrangePolynomialApproximation,
+ })
+ }
+ this._velocityOrientation = new Cesium.VelocityOrientationProperty(
+ this._sampledPosition
+ )
+ this._endTime = Cesium.JulianDate.addSeconds(
+ this._timeLine[this._timeLine.length - 1],
+ this._options.endDelayTime,
+ new Cesium.JulianDate()
+ )
+ }
+
+ /**
+ * Adds Position
+ * @param position
+ * @param duration
+ * @returns {Track}
+ */
+ addPosition(position, duration) {
+ this._positions.push(Parse.parsePosition(position))
+ this._duration += duration
+ this._resetTimeLine({})
+ return this
+ }
+
+ /**
+ * Sets model
+ * @param modelPath
+ * @param style
+ * @returns {Track}
+ */
+ setModel(modelPath, style) {
+ this._delegate.model = {
+ ...style,
+ uri: modelPath,
+ heightReference: this._options.clampToGround
+ ? Cesium.HeightReference.CLAMP_TO_GROUND
+ : Cesium.HeightReference.NONE,
+ }
+ return this
+ }
+
+ /**
+ * Sets billboard
+ * @param icon
+ * @param style
+ * @returns {Track}
+ */
+ setBillboard(icon, style) {
+ this._delegate.billboard = {
+ ...style,
+ image: icon,
+ heightReference: this._options.clampToGround
+ ? Cesium.HeightReference.CLAMP_TO_GROUND
+ : Cesium.HeightReference.NONE,
+ }
+ return this
+ }
+
+ /**
+ * Sets label
+ * @param text
+ * @param style
+ * @returns {Track}
+ */
+ setLabel(text, style) {
+ this._delegate.label = {
+ ...style,
+ text: text,
+ heightReference: this._options.clampToGround
+ ? Cesium.HeightReference.CLAMP_TO_GROUND
+ : Cesium.HeightReference.NONE,
+ }
+ return this
+ }
+
+ /**
+ *
+ * @param visible
+ * @param style
+ * @returns {Track}
+ */
+ setPath(visible, style = {}) {
+ this._path.show = !!visible
+ Util.merge(this._path.polyline, DEF_PATH_STYLE, style)
+ return this
+ }
+}
+
+export default Track
diff --git a/src/modules/history-track/TrackController.js b/src/modules/history-track/TrackController.js
new file mode 100644
index 00000000..d4534759
--- /dev/null
+++ b/src/modules/history-track/TrackController.js
@@ -0,0 +1,240 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-04-01 10:36:36
+ */
+
+import { Cesium } from '../../namespace'
+import { SceneEventType, TrackEventType } from '../event'
+import State from '../state/State'
+import TrackViewMode from './TrackViewMode'
+
+class TrackController {
+ constructor(viewer) {
+ this._viewer = viewer
+ this._cache = {}
+ this._delegete = new Cesium.CustomDataSource('history-track-layer')
+ this._viewer.dataSources.add(this._delegete)
+ this._activedTrack = undefined
+ this._viewOption = {}
+ this._stopTime = undefined
+ this._state = State.INITIALIZED
+ }
+
+ get delegate() {
+ return this._delegete.entities
+ }
+
+ get state() {
+ return this._state
+ }
+
+ /**
+ * @private
+ */
+ _onPostRender() {
+ Object.keys(this._cache).forEach((key) => {
+ let track = this._cache[key]
+ track.trackEvent &&
+ track.trackEvent.fire(TrackEventType.POST_RENDER, {
+ viewer: this._viewer,
+ viewOption: this._viewOption,
+ })
+ })
+ this._viewer.scene.requestRender()
+ }
+
+ /**
+ *
+ * @param track
+ * @returns {TrackController}
+ */
+ addTrack(track) {
+ if (
+ track &&
+ track.trackEvent &&
+ // eslint-disable-next-line no-prototype-builtins
+ !this._cache.hasOwnProperty(track.trackId)
+ ) {
+ track.trackEvent.fire(TrackEventType.ADD, this)
+ this._cache[track.trackId] = track
+ }
+ return this
+ }
+
+ /**
+ *
+ * @param tracks
+ * @returns {TrackController}
+ */
+ addTracks(tracks) {
+ if (Array.isArray(tracks)) {
+ tracks.forEach((item) => {
+ this.addTrack(item)
+ })
+ }
+ return this
+ }
+
+ /**
+ * Returns a track
+ * @param id
+ * @returns {*|undefined}
+ */
+ getTrack(id) {
+ let filters = this.getTracks().filter((item) => item.id === id)
+ return filters && filters.length ? filters[0] : undefined
+ }
+
+ /**
+ * Removes a track
+ * @param track
+ * @returns {TrackController}
+ */
+ removeTrack(track) {
+ if (
+ track &&
+ track.trackEvent &&
+ // eslint-disable-next-line no-prototype-builtins
+ this._cache.hasOwnProperty(track.trackId)
+ ) {
+ track.trackEvent.fire(TrackEventType.REMOVE, this)
+ delete this._cache[track.trackId]
+ }
+ return this
+ }
+
+ /**
+ *
+ * @returns {*[]}
+ */
+ getTracks() {
+ let result = []
+ Object.keys(this._cache).forEach((key) => {
+ result.push(this._cache[key])
+ })
+ return result
+ }
+
+ /**
+ * Starts play all path
+ * @returns {TrackController}
+ */
+ play() {
+ let now = Cesium.JulianDate.now()
+ Object.keys(this._cache).forEach((key) => {
+ let track = this._cache[key]
+ track.startTime = now
+ track.viewed = false
+ })
+ this._activedTrack = undefined
+ this._stopTime = undefined
+ this._viewer.off(SceneEventType.POST_RENDER, this._onPostRender, this)
+ this._viewer.on(SceneEventType.POST_RENDER, this._onPostRender, this)
+ this._state = State.PLAY
+ return this
+ }
+
+ /**
+ *
+ */
+ pause() {
+ this._stopTime = Cesium.JulianDate.now()
+ this._viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)
+ this._viewer.delegate.trackedEntity = undefined
+ this._viewer.off(SceneEventType.POST_RENDER, this._onPostRender, this)
+ this._state = State.PAUSE
+ return this
+ }
+
+ /**
+ *
+ */
+ restore() {
+ if (this._state !== State.PAUSE) {
+ return this
+ }
+ if (this._stopTime) {
+ let now = Cesium.JulianDate.now()
+ Object.keys(this._cache).forEach((key) => {
+ let track = this._cache[key]
+ track.trackEvent.fire(TrackEventType.RESET_TIME_LINE, {
+ stopTime: this._stopTime,
+ duration: Cesium.JulianDate.secondsDifference(now, this._stopTime),
+ })
+ })
+ }
+ this._viewer.off(SceneEventType.POST_RENDER, this._onPostRender, this)
+ this._viewer.on(SceneEventType.POST_RENDER, this._onPostRender, this)
+ this._state = State.PLAY
+ return this
+ }
+
+ // /**
+ // *
+ // * @param speed
+ // * @returns {TrackController}
+ // */
+ // changeSpeed(speed) {
+ // this._viewer.clock.multiplier = speed
+ // return this
+ // }
+
+ /**
+ *
+ * @param track
+ * @param viewOption
+ * @returns {TrackController}
+ */
+ viewTrack(track, viewOption = {}) {
+ // eslint-disable-next-line no-prototype-builtins
+ if (!this._cache.hasOwnProperty(track.trackId)) {
+ throw new Error('TrackController: track does not added ')
+ }
+ this._viewOption = viewOption
+ if (this._activedTrack) {
+ this._activedTrack.viewed = false
+ }
+ track.viewed = true
+ this._activedTrack = track
+ if (viewOption.mode === TrackViewMode.FREE) {
+ this._viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)
+ this._viewer.delegate.trackedEntity = undefined
+ }
+ return this
+ }
+
+ /**
+ *
+ * @param track
+ * @returns {TrackController}
+ */
+ releaseTrack(track) {
+ // eslint-disable-next-line no-prototype-builtins
+ if (!this._cache.hasOwnProperty(track.trackId)) {
+ throw new Error('TrackController: track does not added ')
+ }
+ if (track.viewed) {
+ track.viewed = false
+ }
+ this._activedTrack = undefined
+ this._viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)
+ this._viewer.delegate.trackedEntity = undefined
+ return this
+ }
+
+ /**
+ *
+ * @returns {TrackController}
+ */
+ clear() {
+ Object.keys(this._cache).forEach((key) => {
+ this.removeTrack(this._cache[key])
+ })
+ this._activedTrack && (this._activedTrack.viewed = false)
+ this._activedTrack = undefined
+ this._viewer.off(SceneEventType.POST_RENDER, this._onPostRender, this)
+ return this
+ }
+}
+
+export default TrackController
diff --git a/src/modules/history-track/TrackViewMode.js b/src/modules/history-track/TrackViewMode.js
new file mode 100644
index 00000000..18238b4d
--- /dev/null
+++ b/src/modules/history-track/TrackViewMode.js
@@ -0,0 +1,13 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-05 10:51:13
+ */
+
+const TrackViewMode = {
+ FP: '1',
+ TP: '2',
+ TRACKED: 'tracked',
+ FREE: 'free',
+}
+
+export default TrackViewMode
diff --git a/src/modules/history-track/index.js b/src/modules/history-track/index.js
new file mode 100644
index 00000000..39e70cb7
--- /dev/null
+++ b/src/modules/history-track/index.js
@@ -0,0 +1,8 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-04-13 09:32:31
+ */
+
+export { default as TrackController } from './TrackController'
+export { default as Track } from './Track'
+export { default as TrackViewMode } from './TrackViewMode'
diff --git a/src/modules/icons/compass-inner.js b/src/modules/icons/compass-inner.js
new file mode 100644
index 00000000..99619213
--- /dev/null
+++ b/src/modules/icons/compass-inner.js
@@ -0,0 +1,26 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-06-06 14:05:25
+ */
+
+const compass_inner = `
+
+
+
+ compass-inner
+ Created with Sketch.
+
+
+
+
+
+
+
+
+
+
+
+
+`
+
+export default compass_inner
diff --git a/src/modules/icons/compass-outer.js b/src/modules/icons/compass-outer.js
new file mode 100644
index 00000000..cf60798d
--- /dev/null
+++ b/src/modules/icons/compass-outer.js
@@ -0,0 +1,29 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-06-05 16:01:22
+ */
+
+const compass_outer = `
+
+
+
+ compass-outer
+ Created with Sketch.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`
+
+export default compass_outer
diff --git a/src/modules/icons/compass-rotation-marker.js b/src/modules/icons/compass-rotation-marker.js
new file mode 100644
index 00000000..1318b375
--- /dev/null
+++ b/src/modules/icons/compass-rotation-marker.js
@@ -0,0 +1,22 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-06-06 14:11:56
+ */
+
+const compass_rotation_marker = `
+
+
+
+ compass-rotation-marker
+ Created with Sketch.
+
+
+
+
+
+
+
+
+`
+
+export default compass_rotation_marker
diff --git a/src/modules/icons/decrease.js b/src/modules/icons/decrease.js
new file mode 100644
index 00000000..392c89be
--- /dev/null
+++ b/src/modules/icons/decrease.js
@@ -0,0 +1,14 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-06-06 14:18:00
+ */
+
+const decrease = `
+
+
+
+ decrease
+
+
+`
+export default decrease
diff --git a/src/modules/icons/increase.js b/src/modules/icons/increase.js
new file mode 100644
index 00000000..76a27b34
--- /dev/null
+++ b/src/modules/icons/increase.js
@@ -0,0 +1,15 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-06-06 14:18:00
+ */
+
+const increase = `
+
+
+
+ increase
+
+
+`
+
+export default increase
diff --git a/src/modules/icons/index.js b/src/modules/icons/index.js
new file mode 100644
index 00000000..649f85bb
--- /dev/null
+++ b/src/modules/icons/index.js
@@ -0,0 +1,26 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-06-05 16:35:22
+ */
+
+import logo from './logo'
+import compass_outer from './compass-outer'
+import compass_inner from './compass-inner'
+import compass_rotation_marker from './compass-rotation-marker'
+import decrease from './decrease'
+import increase from './increase'
+import refresh from './refresh'
+import splitter from './splitter'
+
+const Icons = {
+ logo,
+ compass_outer,
+ compass_inner,
+ compass_rotation_marker,
+ decrease,
+ increase,
+ refresh,
+ splitter,
+}
+
+export default Icons
diff --git a/src/modules/icons/logo.js b/src/modules/icons/logo.js
new file mode 100644
index 00000000..3629ce50
--- /dev/null
+++ b/src/modules/icons/logo.js
@@ -0,0 +1,9 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-12-02 19:59:35
+ */
+
+const logo = `
+data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAG5ElEQVRoQ+2Yf2xVZxnHP89py9qliPe0BUTqBrunuDEBx8QfcxuwRYcxM3HO7Y8JZUNKTweKmWyaGDBZYtwSMmnvuXRKcEMzt0WmwdllE1nclkgsyUaIUM4ZNmKihfbcDjbCpPc85rT3QG1677ltb5UlPf+e932e7/f5vu/zPO8jfMA/+YDjZ4rA/1vBKQWmFBgWAXOnex0Bu4FPAf8Evu3b1vOFgnTZHKHa1LEVgZTtEoLv9NkLfpMjs03hfMa2VucjcdkQSKTdVkGq/ebk2ghswjmxSMh2+Lb10cufgOO9AHRk7OSTw8Gajqu+beUN9GWjgJnq2qZi3JqxrRURATPtbka5w7etL066AjXprmUaGDdhyCI0WAhyHeAjHFPoMgI9lDWyr/Y3X9udD4yZcg8j2qMiewVZhmpjoeiHdiakgJny6tXQLaJ8FZiTA/auCD2q9IBWozILYdZF0KoHBXb2tTT8fDQiNanjjSpyCxr83W9ZsC0uTY+bQE3Ke1BFtwD14dkVpMNQOk63JL1RgbV13RYYxkqEO0S5AfQPasiOzAZrXxzIkqfR8LwixlaBfYEYT2aar/ld0SC2HplmzqzchKHfQpkLfD0u15eUgOl4z4DeiwY/LCSxmXIf8FusXXnP+1DReg5YCGz2beuJooMwbOGYjpDpuPuBlSrcl2m2fjncYV3KS2bJ3heSSqRcW4QUBgv9DdZfCwEzHffPwKfjAjLhLJRw3DaBltEkr3G8jYp+H5gdZFnRv9F6NZH2bso0J98oJqoJxz0gsFyRppF1IG5/UQok0t69ovoMsNu3rfsvVcqumwXje8AqlFcC1Uf7H2z4U5zTkf9n/vTErIELAy+D1BplFTf2Nl0d9kFFfbEE5rR3Xvl+dsbrCvUGZZ/vted3hZYTjrdK0N8DZxR5NGMnHy/KY55FCcdbL2j7WI9SLIEo46jqw5mWhsdC/2bK+xwS7BMkm1X9Qn9Lw5sTAX9JUbdT0HmixvasBq8FlVVHzqyr9yeUhUzHC1Pk9b6dvHow8mnvelRfEEgi+jW/ueHXpQAf2sjVltb/tqdvgfEySqvfkjw50ldhBQ5ouXnMe0+U5/ps6xuD0XeOt4OsV3gkY1s/LhX4Qds73A9Rxr8QeUNEn1aoRfkscLcKbZlma+OYCHy41V1ulHEgyjy1jrc0QDtBXvPt5C2lBB/ZMh33Z8ADw3ugMEsZQn1fs5UcE4EonytlizP2/MNR9Et9dIaDqkl7m1T1J/8eYOa7m6zTQ6q7YeKo8m3rY2MlkBLBLq8om33qm/N7wt4cOOjb1mcmI/q5BHEPor+KimBN2v2RKo+Iysa+lmTb2AjkCkwkp5k6/oML5Zo+27Sgd7IIRMc2LIiGESwPe65CqbXgJTZT3k5EmyIFJgv0cLtmyhtSAMK7sA70dd9uuDmf7xgC7maE7dEd+J8QyF3iQV8i+/3m5O3jrgOJtq4viWG8OJmX9mIRaz36CcrKHxLITSB0t283XGxbxqdAmJfLeQfY5dvWuslQYO72k1Xnqt5/CA2+CzId9ALIId+2wvwf+8W3Eo7bA1T4tmXGWhvDgrrUkeqAikbEuF/hk8CbKnJQVJtGa9fHp4Dj/gMYnMmI6tp879gx4A6r7Vwp18YAWTPYjsBJgXTV+dNPnKus2wuyZGDaFQvjeqDIZ14FEk7YWLFUIWwX7hKkPJALKwpNFQoRCXsoUdaANjLUInSKIXsGtGLPO/ZVmZqUt1pFnxKRx/qakw8XG5RRCSRS7mABi/Jv7sGyY+R7IM5JTXvXx4MBY6UIYdtxF1AO+iJq7PFbks9G+4e9B67FYEncK26433wETolQN7wfMR33JSAcMBV8hNc4b9+uol9G9VZgSc7ZKcIBQJZfhK+1kcSjFxno477dEE46iv5GJWA67pHQgoGs6bWThyJrpuN1g14VXbLa9u6PZIPsUgkGbhQxblDkNtArh9ZLN8JvA9U/XlE9fX/P6tnvjYYqehOHE44+27qzaOS5hXkUOL5FRIZaZeUVhDOonkWkH9gAVAJngekjHB5V0b2GUbGvr2newUJgctPnaCrxkm9bq8YKfjBM+TaZjnu3CMtVWQwsC1PppbVyLhfpEwodUPbsQFnl0bNNc+J7pJFzoZjxTByp2DowaGDrgfLaurmLVINz06undXevnXf+Yms95GEckzn+hgZPFzM+LESiOAJ5LAw9cIL1IPcAM3JHrgfhLdDDiswSdDHIoksm9C8Ce3RAnvI3WWfiIhz3f0IEIuOJ9rdnGEFwp6p+BeQaoA50JoRHjT6gV5VODXh+tCwUB3LSFJiI41LtLYkCpQIzHjtTBMYTtVLumVKglNEcj63/AIrz7E/FBbRAAAAAAElFTkSuQmCC
+`
+export default logo
diff --git a/src/modules/icons/refresh.js b/src/modules/icons/refresh.js
new file mode 100644
index 00000000..2441cd65
--- /dev/null
+++ b/src/modules/icons/refresh.js
@@ -0,0 +1,15 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-06-06 14:18:00
+ */
+
+const refresh = `
+
+
+
+ refresh
+
+
+`
+
+export default refresh
diff --git a/src/modules/icons/splitter.js b/src/modules/icons/splitter.js
new file mode 100644
index 00000000..63730d61
--- /dev/null
+++ b/src/modules/icons/splitter.js
@@ -0,0 +1,13 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-06-06 14:18:00
+ */
+
+const splitter = `
+
+
+
+
+`
+
+export default splitter
diff --git a/src/modules/imagery/ImageryLayerFactory.js b/src/modules/imagery/ImageryLayerFactory.js
new file mode 100644
index 00000000..2347ac29
--- /dev/null
+++ b/src/modules/imagery/ImageryLayerFactory.js
@@ -0,0 +1,211 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-21 15:54:56
+ */
+
+import { Cesium } from '../../namespace'
+import ImageryType from './ImageryType'
+import AmapImageryProvider from './provider/AmapImageryProvider'
+import BaiduImageryProvider from './provider/BaiduImageryProvider'
+import GoogleImageryProvider from './provider/GoogleImageryProvider'
+import TdtImageryProvider from './provider/TdtImageryProvider'
+import TencentImageryProvider from './provider/TencentImageryProvider'
+
+class ImageryLayerFactory {
+ /**
+ * Create amap image layer
+ * @param options
+ * @returns {AmapImageryProvider}
+ */
+ static createAmapImageryLayer(options) {
+ return new AmapImageryProvider(options)
+ }
+
+ /**
+ * Create baidu image layer
+ * @param options
+ * @returns {BaiduImageryProvider}
+ */
+ static createBaiduImageryLayer(options) {
+ return new BaiduImageryProvider(options)
+ }
+
+ /**
+ * Create google image layer
+ * @param options
+ * @returns {GoogleImageryProvider}
+ */
+ static createGoogleImageryLayer(options) {
+ return new GoogleImageryProvider(options)
+ }
+
+ /**
+ * Create tdt image layer
+ * @param options
+ * @returns {TdtImageryProvider}
+ */
+ static createTdtImageryLayer(options) {
+ return new TdtImageryProvider(options)
+ }
+
+ /**
+ * Create tencent image layer
+ * @param options
+ * @returns {TencentImageryProvider}
+ */
+ static createTencentImageryLayer(options) {
+ return new TencentImageryProvider(options)
+ }
+
+ /**
+ * Create arcgis image layer
+ * @param options
+ * @returns {module:cesium.ArcGisMapServerImageryProvider}
+ */
+ static createArcGisImageryLayer(options) {
+ return new Cesium.ArcGisMapServerImageryProvider(options)
+ }
+
+ /**
+ * Create single tile image layer
+ * @param options
+ * @returns {module:cesium.SingleTileImageryProvider}
+ */
+ static createSingleTileImageryLayer(options) {
+ return new Cesium.SingleTileImageryProvider(options)
+ }
+
+ /**
+ * Create WMS image layer
+ * @param options
+ * @returns {module:cesium.WebMapServiceImageryProvider}
+ */
+ static createWMSImageryLayer(options) {
+ return new Cesium.WebMapServiceImageryProvider(options)
+ }
+
+ /**
+ * Create WMTS image layer
+ * @param options
+ * @returns {module:cesium.WebMapTileServiceImageryProvider}
+ */
+ static createWMTSImageryLayer(options) {
+ return new Cesium.WebMapTileServiceImageryProvider(options)
+ }
+
+ /**
+ * Create xyz image layer
+ * @param options
+ * @returns {module:cesium.UrlTemplateImageryProvider}
+ */
+ static createXYZImageryLayer(options) {
+ return new Cesium.UrlTemplateImageryProvider(options)
+ }
+
+ /**
+ * Create coord image layer
+ * @param options
+ * @returns {module:cesium.TileCoordinatesImageryProvider}
+ */
+ static createCoordImageryLayer(options) {
+ return new Cesium.TileCoordinatesImageryProvider(options)
+ }
+
+ /**
+ * Create grid image layer
+ * @param options
+ * @returns {module:cesium.GridImageryProvider}
+ */
+ static createGridImageryLayer(options) {
+ return new Cesium.GridImageryProvider(options)
+ }
+
+ /**
+ * Create mapbox image layer
+ * @param options
+ * @returns {module:cesium.MapboxImageryProvider}
+ */
+ static createMapboxImageryLayer(options) {
+ return new Cesium.MapboxImageryProvider(options)
+ }
+
+ /**
+ * Create mapbox style image layer
+ * @param options
+ * @returns {module:cesium.MapboxStyleImageryProvider}
+ */
+ static createMapboxStyleImageryLayer(options) {
+ return new Cesium.MapboxStyleImageryProvider(options)
+ }
+
+ /**
+ * Create TMS image layer
+ * @param options
+ * @returns {module:cesium.TileMapServiceImageryProvider}
+ */
+ static createTMSImageryLayer(options) {
+ return new Cesium.TileMapServiceImageryProvider(options)
+ }
+
+ /**
+ * Create Imagery Layer
+ * @param type
+ * @param options
+ * @returns {any}
+ */
+ static createImageryLayer(type, options) {
+ let imageryLayer = undefined
+ switch (type) {
+ case ImageryType.AMAP:
+ imageryLayer = this.createAmapImageryLayer(options)
+ break
+ case ImageryType.BAIDU:
+ imageryLayer = this.createBaiduImageryLayer(options)
+ break
+ case ImageryType.GOOGLE:
+ imageryLayer = this.createGoogleImageryLayer(options)
+ break
+ case ImageryType.TDT:
+ imageryLayer = this.createTdtImageryLayer(options)
+ break
+ case ImageryType.TENCENT:
+ imageryLayer = this.createTencentImageryLayer(options)
+ break
+ case ImageryType.ARCGIS:
+ imageryLayer = this.createArcGisImageryLayer(options)
+ break
+ case ImageryType.SINGLE_TILE:
+ imageryLayer = this.createSingleTileImageryLayer(options)
+ break
+ case ImageryType.WMS:
+ imageryLayer = this.createWMSImageryLayer(options)
+ break
+ case ImageryType.WMTS:
+ imageryLayer = this.createWMTSImageryLayer(options)
+ break
+ case ImageryType.XYZ:
+ imageryLayer = this.createXYZImageryLayer(options)
+ break
+ case ImageryType.COORD:
+ imageryLayer = this.createCoordImageryLayer(options)
+ break
+ case ImageryType.GRID:
+ imageryLayer = this.createGridImageryLayer(options)
+ break
+ case ImageryType.MAPBOX:
+ imageryLayer = this.createMapboxImageryLayer(options)
+ break
+ case ImageryType.MAPBOX_STYLE:
+ imageryLayer = this.createMapboxStyleImageryLayer(options)
+ break
+ case ImageryType.TMS:
+ imageryLayer = this.createTMSImageryLayer(options)
+ break
+ default:
+ break
+ }
+ return imageryLayer
+ }
+}
+
+export default ImageryLayerFactory
diff --git a/src/modules/imagery/ImageryType.js b/src/modules/imagery/ImageryType.js
new file mode 100644
index 00000000..112ae2e9
--- /dev/null
+++ b/src/modules/imagery/ImageryType.js
@@ -0,0 +1,19 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-05-10 08:15:36
+ */
+
+let ImageryType = {
+ ARCGIS: 'arcgis',
+ SINGLE_TILE: 'single_tile',
+ WMS: 'wms',
+ WMTS: 'wmts',
+ XYZ: 'xyz',
+ COORD: 'coord',
+ GRID: 'grid',
+ MAPBOX: 'mapbox',
+ MAPBOX_STYLE: 'mapbox_style',
+ TMS: 'tms',
+}
+
+export default ImageryType
diff --git a/src/modules/imagery/index.js b/src/modules/imagery/index.js
new file mode 100644
index 00000000..c158bb0a
--- /dev/null
+++ b/src/modules/imagery/index.js
@@ -0,0 +1,7 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-03-14 00:31:46
+ */
+
+export { default as ImageryType } from './ImageryType'
+export { default as ImageryLayerFactory } from './ImageryLayerFactory'
diff --git a/src/modules/imagery/projection/BaiduMercatorProjection.js b/src/modules/imagery/projection/BaiduMercatorProjection.js
new file mode 100644
index 00000000..c540c2dc
--- /dev/null
+++ b/src/modules/imagery/projection/BaiduMercatorProjection.js
@@ -0,0 +1,414 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-01-30 22:41:41
+ */
+
+const EARTH_RADIUS = 6370996.81
+const MC_BAND = [12890594.86, 8362377.87, 5591021, 3481989.83, 1678043.12, 0]
+const LL_BAND = [75, 60, 45, 30, 15, 0]
+const MC2LL = [
+ [
+ 1.410526172116255e-8, 8.98305509648872e-6, -1.9939833816331,
+ 2.009824383106796e2, -1.872403703815547e2, 91.6087516669843,
+ -23.38765649603339, 2.57121317296198, -0.03801003308653, 1.73379812e7,
+ ],
+ [
+ -7.435856389565537e-9, 8.983055097726239e-6, -0.78625201886289,
+ 96.32687599759846, -1.85204757529826, -59.36935905485877, 47.40033549296737,
+ -16.50741931063887, 2.28786674699375, 1.026014486e7,
+ ],
+ [
+ -3.030883460898826e-8, 8.98305509983578e-6, 0.30071316287616,
+ 59.74293618442277, 7.357984074871, -25.38371002664745, 13.45380521110908,
+ -3.29883767235584, 0.32710905363475, 6.85681737e6,
+ ],
+ [
+ -1.981981304930552e-8, 8.983055099779535e-6, 0.03278182852591,
+ 40.31678527705744, 0.65659298677277, -4.44255534477492, 0.85341911805263,
+ 0.12923347998204, -0.04625736007561, 4.48277706e6,
+ ],
+ [
+ 3.09191371068437e-9, 8.983055096812155e-6, 0.00006995724062,
+ 23.10934304144901, -0.00023663490511, -0.6321817810242, -0.00663494467273,
+ 0.03430082397953, -0.00466043876332, 2.5551644e6,
+ ],
+ [
+ 2.890871144776878e-9, 8.983055095805407e-6, -0.00000003068298,
+ 7.47137025468032, -0.00000353937994, -0.02145144861037, -0.00001234426596,
+ 0.00010322952773, -0.00000323890364, 8.260885e5,
+ ],
+]
+const LL2MC = [
+ [
+ -0.0015702102444, 1.113207020616939e5, 1.704480524535203e15,
+ -1.033898737604234e16, 2.611266785660388e16, -3.51496691766537e16,
+ 2.659570071840392e16, -1.072501245418824e16, 1.800819912950474e15, 82.5,
+ ],
+ [
+ 8.277824516172526e-4, 1.113207020463578e5, 6.477955746671608e8,
+ -4.082003173641316e9, 1.077490566351142e10, -1.517187553151559e10,
+ 1.205306533862167e10, -5.124939663577472e9, 9.133119359512032e8, 67.5,
+ ],
+ [
+ 0.00337398766765, 1.113207020202162e5, 4.481351045890365e6,
+ -2.339375119931662e7, 7.968221547186455e7, -1.159649932797253e8,
+ 9.723671115602145e7, -4.366194633752821e7, 8.477230501135234e6, 52.5,
+ ],
+ [
+ 0.00220636496208, 1.113207020209128e5, 5.175186112841131e4,
+ 3.796837749470245e6, 9.920137397791013e5, -1.22195221711287e6,
+ 1.340652697009075e6, -6.209436990984312e5, 1.444169293806241e5, 37.5,
+ ],
+ [
+ -3.441963504368392e-4, 1.113207020576856e5, 2.782353980772752e2,
+ 2.485758690035394e6, 6.070750963243378e3, 5.482118345352118e4,
+ 9.540606633304236e3, -2.71055326746645e3, 1.405483844121726e3, 22.5,
+ ],
+ [
+ -3.218135878613132e-4, 1.113207020701615e5, 0.00369383431289,
+ 8.237256402795718e5, 0.46104986909093, 2.351343141331292e3,
+ 1.58060784298199, 8.77738589078284, 0.37238884252424, 7.45,
+ ],
+]
+
+class BaiduMercatorProjection {
+ constructor() {
+ this.isWgs84 = false
+ }
+
+ /**
+ *
+ * @param point1
+ * @param point2
+ * @returns {number}
+ */
+ getDistanceByMC(point1, point2) {
+ if (!point1 || !point2) {
+ return 0
+ }
+ point1 = this.convertMC2LL(point1)
+ if (!point1) {
+ return 0
+ }
+ let x1 = this.toRadians(point1['lng'])
+ let y1 = this.toRadians(point1['lat'])
+ point2 = this.convertMC2LL(point2)
+ if (!point2) {
+ return 0
+ }
+ let x2 = this.toRadians(point2['lng'])
+ let y2 = this.toRadians(point2['lat'])
+ return this.getDistance(x1, x2, y1, y2)
+ }
+
+ /**
+ * Calculate the distance between two points according to the latitude and longitude coordinates
+ * @param point1
+ * @param point2
+ * @returns {number|*}
+ */
+ getDistanceByLL(point1, point2) {
+ if (!point1 || !point2) {
+ return 0
+ }
+ point1['lng'] = this.getLoop(point1['lng'], -180, 180)
+ point1['lat'] = this.getRange(point1['lat'], -74, 74)
+ point2['lng'] = this.getLoop(point2['lng'], -180, 180)
+ point2['lat'] = this.getRange(point2['lat'], -74, 74)
+ let x1 = this.toRadians(point1['lng'])
+ let y1 = this.toRadians(point1['lat'])
+ let x2 = this.toRadians(point2['lng'])
+ let y2 = this.toRadians(point2['lat'])
+ return this.getDistance(x1, x2, y1, y2)
+ }
+
+ /**
+ * The plane cartesian coordinates are converted to latitude and longitude coordinates
+ * @param point
+ * @returns {Point|{lng: number, lat: number}}
+ */
+ convertMC2LL(point) {
+ if (!point) {
+ return { lng: 0, lat: 0 }
+ }
+ let lnglat = {}
+ if (this.isWgs84) {
+ lnglat.lng = (point.lng / 20037508.34) * 180
+ let mmy = (point.lat / 20037508.34) * 180
+ lnglat.lat =
+ (180 / Math.PI) *
+ (2 * Math.atan(Math.exp((mmy * Math.PI) / 180)) - Math.PI / 2)
+ return {
+ lng: lnglat['lng'].toFixed(6),
+ lat: lnglat['lat'].toFixed(6),
+ }
+ }
+
+ let temp = {
+ lng: Math.abs(point['lng']),
+ lat: Math.abs(point['lat']),
+ }
+
+ let factor = undefined
+ for (let i = 0; i < MC_BAND.length; i++) {
+ if (temp['lat'] >= MC_BAND[i]) {
+ factor = MC2LL[i]
+ break
+ }
+ }
+ lnglat = this.convertor(point, factor)
+ return {
+ lng: lnglat['lng'].toFixed(6),
+ lat: lnglat['lat'].toFixed(6),
+ }
+ }
+
+ /**
+ * The latitude and longitude coordinates are converted to plane cartesian coordinates
+ * @param point
+ * @returns {{lng: number, lat: number}|*}
+ */
+ convertLL2MC(point) {
+ if (!point) {
+ return { lng: 0, lat: 0 }
+ }
+ if (
+ point['lng'] > 180 ||
+ point['lng'] < -180 ||
+ point['lat'] > 90 ||
+ point['lat'] < -90
+ ) {
+ return point
+ }
+
+ if (this.isWgs84) {
+ let mercator = {}
+ let earthRad = 6378137.0
+ mercator.lng = ((point.lng * Math.PI) / 180) * earthRad
+ let a = (point.lat * Math.PI) / 180
+ mercator.lat =
+ (earthRad / 2) * Math.log((1.0 + Math.sin(a)) / (1.0 - Math.sin(a)))
+
+ return {
+ lng: parseFloat(mercator['lng'].toFixed(2)),
+ lat: parseFloat(mercator['lat'].toFixed(2)),
+ }
+ }
+
+ point['lng'] = this.getLoop(point['lng'], -180, 180)
+ point['lat'] = this.getRange(point['lat'], -74, 74)
+ let temp = { lng: point['lng'], lat: point['lat'] }
+ let factor = undefined
+ for (let i = 0; i < LL_BAND.length; i++) {
+ if (temp['lat'] >= LL_BAND[i]) {
+ factor = LL2MC[i]
+ break
+ }
+ }
+ if (!factor) {
+ for (let i = 0; i < LL_BAND.length; i++) {
+ if (temp['lat'] <= -LL_BAND[i]) {
+ factor = LL2MC[i]
+ break
+ }
+ }
+ }
+ let mc = this.convertor(point, factor)
+ return {
+ lng: parseFloat(mc['lng'].toFixed(2)),
+ lat: parseFloat(mc['lat'].toFixed(2)),
+ }
+ }
+
+ /**
+ *
+ * @param fromPoint
+ * @param factor
+ * @returns {{lng: *, lat: *}}
+ */
+ convertor(fromPoint, factor) {
+ if (!fromPoint || !factor) {
+ return { lng: 0, lat: 0 }
+ }
+ let x = factor[0] + factor[1] * Math.abs(fromPoint['lng'])
+ let temp = Math.abs(fromPoint['lat']) / factor[9]
+ let y =
+ factor[2] +
+ factor[3] * temp +
+ factor[4] * temp * temp +
+ factor[5] * temp * temp * temp +
+ factor[6] * temp * temp * temp * temp +
+ factor[7] * temp * temp * temp * temp * temp +
+ factor[8] * temp * temp * temp * temp * temp * temp
+ x *= fromPoint['lng'] < 0 ? -1 : 1
+ y *= fromPoint['lat'] < 0 ? -1 : 1
+ return {
+ lng: x,
+ lat: y,
+ }
+ }
+
+ /**
+ *
+ * @param x1
+ * @param x2
+ * @param y1
+ * @param y2
+ * @returns {number}
+ */
+ getDistance(x1, x2, y1, y2) {
+ return (
+ EARTH_RADIUS *
+ Math.acos(
+ Math.sin(y1) * Math.sin(y2) +
+ Math.cos(y1) * Math.cos(y2) * Math.cos(x2 - x1)
+ )
+ )
+ }
+
+ /**
+ *
+ * @param deg
+ * @returns {number}
+ */
+ toRadians(deg) {
+ return (Math.PI * deg) / 180
+ }
+
+ /**
+ *
+ * @param rad
+ * @returns {number}
+ */
+ toDegrees(rad) {
+ return (180 * rad) / Math.PI
+ }
+
+ /**
+ *
+ * @param v
+ * @param a
+ * @param b
+ * @returns {number}
+ */
+ getRange(v, a, b) {
+ if (a != null) {
+ v = Math.max(v, a)
+ }
+ if (b != null) {
+ v = Math.min(v, b)
+ }
+ return v
+ }
+
+ /**
+ *
+ * @param v
+ * @param a
+ * @param b
+ * @returns {*}
+ */
+ getLoop(v, a, b) {
+ while (v > b) {
+ v -= b - a
+ }
+ while (v < a) {
+ v += b - a
+ }
+ return v
+ }
+
+ /**
+ *
+ * @param point
+ * @returns {{lng: number, lat: number}|*}
+ */
+ lngLatToMercator(point) {
+ return this.convertLL2MC(point)
+ }
+
+ /**
+ *
+ * @param point
+ * @returns {{x: (number|*), y: (number|*)}}
+ */
+ lngLatToPoint(point) {
+ let mercator = this.convertLL2MC(point)
+ return {
+ x: mercator['lng'],
+ y: mercator['lat'],
+ }
+ }
+
+ /**
+ * WebMercator transforms to latitude and longitude
+ * @param point
+ * @returns {Point|{lng: number, lat: number}}
+ */
+ mercatorToLngLat(point) {
+ return this.convertMC2LL(point)
+ }
+
+ /**
+ *
+ * @param point
+ * @returns {Point|{lng: number, lat: number}}
+ */
+ pointToLngLat(point) {
+ let mercator = { lng: point.x, lat: point.y }
+ return this.convertMC2LL(mercator)
+ }
+
+ /**
+ * Latitude and longitude coordinates transforms to pixel coordinates
+ * @param point
+ * @param zoom
+ * @param mapCenter
+ * @param mapSize
+ * @returns {{x: number, y: number}}
+ */
+ pointToPixel(point, zoom, mapCenter, mapSize) {
+ if (!point) {
+ return { x: 0, y: 0 }
+ }
+ point = this.lngLatToMercator(point)
+ let zoomUnits = this.getZoomUnits(zoom)
+ let x = Math.round(
+ (point['lng'] - mapCenter['lng']) / zoomUnits + mapSize.width / 2
+ )
+ let y = Math.round(
+ (mapCenter['lat'] - point['lat']) / zoomUnits + mapSize.height / 2
+ )
+ return { x, y }
+ }
+
+ /**
+ * Pixel coordinates transforms to latitude and longitude coordinates
+ * @param pixel
+ * @param zoom
+ * @param mapCenter
+ * @param mapSize
+ * @returns {Point|{lng: number, lat: number}}
+ */
+ pixelToPoint(pixel, zoom, mapCenter, mapSize) {
+ if (!pixel) {
+ return { lng: 0, lat: 0 }
+ }
+ let zoomUnits = this.getZoomUnits(zoom)
+ let lng = mapCenter['lng'] + zoomUnits * (pixel.x - mapSize.width / 2)
+ let lat = mapCenter['lat'] - zoomUnits * (pixel.y - mapSize.height / 2)
+ let point = { lng, lat }
+ return this.mercatorToLngLat(point)
+ }
+
+ /**
+ *
+ * @param zoom
+ * @returns {number}
+ */
+ getZoomUnits(zoom) {
+ return Math.pow(2, 18 - zoom)
+ }
+}
+
+export default BaiduMercatorProjection
diff --git a/src/modules/imagery/provider/AmapImageryProvider.js b/src/modules/imagery/provider/AmapImageryProvider.js
new file mode 100644
index 00000000..f07ff5e7
--- /dev/null
+++ b/src/modules/imagery/provider/AmapImageryProvider.js
@@ -0,0 +1,34 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-15 20:31:28
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+import ImageryType from '../ImageryType'
+import AmapMercatorTilingScheme from '../tiling-scheme/AmapMercatorTilingScheme'
+
+const TILE_URL = {
+ img: '//webst{s}.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}',
+ elec: '//webrd{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
+ cva: '//webst{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
+}
+
+class AmapImageryProvider extends Cesium.UrlTemplateImageryProvider {
+ constructor(options = {}) {
+ options['url'] =
+ options.url ||
+ [
+ options.protocol || '',
+ TILE_URL[options.style] || TILE_URL['elec'],
+ ].join('')
+ options['subdomains'] = options.subdomains || ['01', '02', '03', '04']
+ if (options.crs === 'WGS84') {
+ options['tilingScheme'] = new AmapMercatorTilingScheme()
+ }
+ super(options)
+ }
+}
+
+ImageryType.AMAP = 'amap'
+
+export default AmapImageryProvider
diff --git a/src/modules/imagery/provider/BaiduImageryProvider.js b/src/modules/imagery/provider/BaiduImageryProvider.js
new file mode 100644
index 00000000..be823a69
--- /dev/null
+++ b/src/modules/imagery/provider/BaiduImageryProvider.js
@@ -0,0 +1,176 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-15 20:27:27
+ */
+
+import { Cesium } from '../../../namespace'
+import ImageryType from '../ImageryType'
+import BaiduMercatorTilingScheme from '../tiling-scheme/BaiduMercatorTilingScheme'
+
+const TILE_URL = {
+ img: '//shangetu{s}.map.bdimg.com/it/u=x={x};y={y};z={z};v=009;type=sate&fm=46',
+ vec: '//online{s}.map.bdimg.com/tile/?qt=tile&x={x}&y={y}&z={z}&styles=sl&v=020',
+ custom:
+ '//api{s}.map.bdimg.com/customimage/tile?&x={x}&y={y}&z={z}&scale=1&customid={style}',
+ traffic:
+ '//its.map.baidu.com:8002/traffic/TrafficTileService?time={time}&label={labelStyle}&v=016&level={z}&x={x}&y={y}&scaler=2',
+}
+
+class BaiduImageryProvider {
+ constructor(options = {}) {
+ this._url =
+ options.url ||
+ [
+ options.protocol || '',
+ TILE_URL[options.style] || TILE_URL['custom'],
+ ].join('')
+ this._labelStyle = options.labelStyle || 'web2D'
+ this._tileWidth = 256
+ this._tileHeight = 256
+ this._maximumLevel = 18
+ this._crs = options.crs || 'BD09'
+ if (options.crs === 'WGS84') {
+ let resolutions = []
+ for (let i = 0; i < 19; i++) {
+ resolutions[i] = 256 * Math.pow(2, 18 - i)
+ }
+ this._tilingScheme = new BaiduMercatorTilingScheme({
+ resolutions,
+ rectangleSouthwestInMeters: new Cesium.Cartesian2(
+ -20037726.37,
+ -12474104.17
+ ),
+ rectangleNortheastInMeters: new Cesium.Cartesian2(
+ 20037726.37,
+ 12474104.17
+ ),
+ })
+ } else {
+ this._tilingScheme = new Cesium.WebMercatorTilingScheme({
+ rectangleSouthwestInMeters: new Cesium.Cartesian2(-33554054, -33746824),
+ rectangleNortheastInMeters: new Cesium.Cartesian2(33554054, 33746824),
+ })
+ }
+ this._rectangle = this._tilingScheme.rectangle
+ this._credit = undefined
+ this._token = undefined
+ this._style = options.style || 'normal'
+ this._errorEvent = new Cesium.Event()
+ }
+
+ get url() {
+ return this._url
+ }
+
+ get token() {
+ return this._token
+ }
+
+ get tileWidth() {
+ if (!this.ready) {
+ throw new Cesium.DeveloperError(
+ 'tileWidth must not be called before the imagery provider is ready.'
+ )
+ }
+ return this._tileWidth
+ }
+
+ get tileHeight() {
+ if (!this.ready) {
+ throw new Cesium.DeveloperError(
+ 'tileHeight must not be called before the imagery provider is ready.'
+ )
+ }
+ return this._tileHeight
+ }
+
+ get maximumLevel() {
+ if (!this.ready) {
+ throw new Cesium.DeveloperError(
+ 'maximumLevel must not be called before the imagery provider is ready.'
+ )
+ }
+ return this._maximumLevel
+ }
+
+ get minimumLevel() {
+ if (!this.ready) {
+ throw new Cesium.DeveloperError(
+ 'minimumLevel must not be called before the imagery provider is ready.'
+ )
+ }
+ return 0
+ }
+
+ get tilingScheme() {
+ if (!this.ready) {
+ throw new Cesium.DeveloperError(
+ 'tilingScheme must not be called before the imagery provider is ready.'
+ )
+ }
+ return this._tilingScheme
+ }
+
+ get rectangle() {
+ if (!this.ready) {
+ throw new Cesium.DeveloperError(
+ 'rectangle must not be called before the imagery provider is ready.'
+ )
+ }
+ return this._rectangle
+ }
+
+ get ready() {
+ return !!this._url
+ }
+
+ get credit() {
+ return this._credit
+ }
+
+ get hasAlphaChannel() {
+ return true
+ }
+
+ get errorEvent() {
+ return this._errorEvent
+ }
+
+ getTileCredits(x, y, level) {}
+
+ /**
+ * Request Image
+ * @param x
+ * @param y
+ * @param level
+ * @returns {Promise}
+ */
+ requestImage(x, y, level) {
+ if (!this.ready) {
+ throw new Cesium.DeveloperError(
+ 'requestImage must not be called before the imagery provider is ready.'
+ )
+ }
+ let xTiles = this._tilingScheme.getNumberOfXTilesAtLevel(level)
+ let yTiles = this._tilingScheme.getNumberOfYTilesAtLevel(level)
+ let url = this._url
+ .replace('{z}', level)
+ .replace('{s}', String(1))
+ .replace('{style}', this._style)
+ .replace('{labelStyle}', this._labelStyle)
+ .replace('{time}', String(new Date().getTime()))
+
+ if (this._crs === 'WGS84') {
+ url = url.replace('{x}', String(x)).replace('{y}', String(-y))
+ } else {
+ url = url
+ .replace('{x}', String(x - xTiles / 2))
+ .replace('{y}', String(yTiles / 2 - y - 1))
+ }
+ return Cesium.ImageryProvider.loadImage(this, url)
+ }
+}
+
+ImageryType.BAIDU = 'baidu'
+
+export default BaiduImageryProvider
diff --git a/src/modules/imagery/provider/GoogleImageryProvider.js b/src/modules/imagery/provider/GoogleImageryProvider.js
new file mode 100644
index 00000000..01011125
--- /dev/null
+++ b/src/modules/imagery/provider/GoogleImageryProvider.js
@@ -0,0 +1,30 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-21 16:06:14
+ */
+
+import { Cesium } from '../../../namespace'
+import ImageryType from '../ImageryType'
+
+const TILE_URL = {
+ img: '//mt{s}.google.cn/vt/lyrs=s&hl=zh-CN&x={x}&y={y}&z={z}&s=Gali',
+ elec: '//mt{s}.google.cn/vt/lyrs=m@207000000&hl=zh-CN&gl=CN&src=app&x={x}&y={y}&z={z}&s=Galile',
+ ter: '//mt{s}.google.cn/vt/lyrs=t@131,r@227000000&hl=zh-CN&gl=cn&x={x}&y={y}&z={z}&s=Galile',
+}
+
+class GoogleImageryProvider extends Cesium.UrlTemplateImageryProvider {
+ constructor(options = {}) {
+ options['url'] =
+ options.url ||
+ [
+ options.protocol || '',
+ TILE_URL[options.style] || TILE_URL['elec'],
+ ].join('')
+ options['subdomains'] = options.subdomains || ['1', '2', '3']
+ super(options)
+ }
+}
+
+ImageryType.GOOGLE = 'google'
+
+export default GoogleImageryProvider
diff --git a/src/modules/imagery/provider/TdtImageryProvider.js b/src/modules/imagery/provider/TdtImageryProvider.js
new file mode 100644
index 00000000..92c64492
--- /dev/null
+++ b/src/modules/imagery/provider/TdtImageryProvider.js
@@ -0,0 +1,30 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-15 20:31:46
+ */
+
+import { Cesium } from '../../../namespace'
+import ImageryType from '../ImageryType'
+
+const MAP_URL =
+ '//t{s}.tianditu.gov.cn/DataServer?T={style}_w&x={x}&y={y}&l={z}&tk={key}'
+
+class TdtImageryProvider extends Cesium.UrlTemplateImageryProvider {
+ constructor(options = {}) {
+ super({
+ url: [
+ options.protocol || '',
+ MAP_URL.replace(/\{style\}/g, options.style || 'vec').replace(
+ /\{key\}/g,
+ options.key || ''
+ ),
+ ].join(''),
+ subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
+ maximumLevel: 18,
+ })
+ }
+}
+
+ImageryType.TDT = 'tdt'
+
+export default TdtImageryProvider
diff --git a/src/modules/imagery/provider/TencentImageryProvider.js b/src/modules/imagery/provider/TencentImageryProvider.js
new file mode 100644
index 00000000..48fe9e70
--- /dev/null
+++ b/src/modules/imagery/provider/TencentImageryProvider.js
@@ -0,0 +1,40 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-21 18:10:47
+ */
+
+import { Cesium } from '../../../namespace'
+import ImageryType from '../ImageryType'
+
+const TILE_URL = {
+ img: '//p{s}.map.gtimg.com/sateTiles/{z}/{sx}/{sy}/{x}_{reverseY}.jpg?version=400',
+ elec: '//rt{s}.map.gtimg.com/tile?z={z}&x={x}&y={reverseY}&styleid={style}&scene=0&version=347',
+}
+
+class TencentImageryProvider extends Cesium.UrlTemplateImageryProvider {
+ constructor(options = {}) {
+ let url =
+ options.url ||
+ [
+ options.protocol || '',
+ TILE_URL[options.style] || TILE_URL['elec'],
+ ].join('')
+ options['url'] = url.replace('{style}', options.style || 1)
+ options['subdomains'] = options.subdomains || ['1', '2', '3']
+ if (options.style === 'img') {
+ options['customTags'] = {
+ sx: (imageryProvider, x, y, level) => {
+ return x >> 4
+ },
+ sy: (imageryProvider, x, y, level) => {
+ return ((1 << level) - y) >> 4
+ },
+ }
+ }
+ super(options)
+ }
+}
+
+ImageryType.TENCENT = 'tencent'
+
+export default TencentImageryProvider
diff --git a/src/modules/imagery/tiling-scheme/AmapMercatorTilingScheme.js b/src/modules/imagery/tiling-scheme/AmapMercatorTilingScheme.js
new file mode 100644
index 00000000..7e68cc6c
--- /dev/null
+++ b/src/modules/imagery/tiling-scheme/AmapMercatorTilingScheme.js
@@ -0,0 +1,40 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-01-31 22:07:05
+ */
+
+import { Cesium } from '../../../namespace'
+import { CoordTransform } from '../../transform'
+
+class AmapMercatorTilingScheme extends Cesium.WebMercatorTilingScheme {
+ constructor(options) {
+ super(options)
+ let projection = new Cesium.WebMercatorProjection()
+ this._projection.project = function (cartographic, result) {
+ result = CoordTransform.WGS84ToGCJ02(
+ Cesium.Math.toDegrees(cartographic.longitude),
+ Cesium.Math.toDegrees(cartographic.latitude)
+ )
+ result = projection.project(
+ new Cesium.Cartographic(
+ Cesium.Math.toRadians(result[0]),
+ Cesium.Math.toRadians(result[1])
+ )
+ )
+ return new Cesium.Cartesian2(result.x, result.y)
+ }
+ this._projection.unproject = function (cartesian, result) {
+ let cartographic = projection.unproject(cartesian)
+ result = CoordTransform.GCJ02ToWGS84(
+ Cesium.Math.toDegrees(cartographic.longitude),
+ Cesium.Math.toDegrees(cartographic.latitude)
+ )
+ return new Cesium.Cartographic(
+ Cesium.Math.toRadians(result[0]),
+ Cesium.Math.toRadians(result[1])
+ )
+ }
+ }
+}
+
+export default AmapMercatorTilingScheme
diff --git a/src/modules/imagery/tiling-scheme/BaiduMercatorTilingScheme.js b/src/modules/imagery/tiling-scheme/BaiduMercatorTilingScheme.js
new file mode 100644
index 00000000..2a5bac24
--- /dev/null
+++ b/src/modules/imagery/tiling-scheme/BaiduMercatorTilingScheme.js
@@ -0,0 +1,102 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-01-31 19:22:04
+ */
+
+import { Cesium } from '../../../namespace'
+import { CoordTransform } from '../../transform'
+import BaiduMercatorProjection from '../projection/BaiduMercatorProjection'
+
+class BaiduMercatorTilingScheme extends Cesium.WebMercatorTilingScheme {
+ constructor(options) {
+ super(options)
+ let projection = new BaiduMercatorProjection()
+ this._projection.project = function (cartographic, result) {
+ result = result || {}
+ result = CoordTransform.WGS84ToGCJ02(
+ Cesium.Math.toDegrees(cartographic.longitude),
+ Cesium.Math.toDegrees(cartographic.latitude)
+ )
+ result = CoordTransform.GCJ02ToBD09(result[0], result[1])
+ result[0] = Math.min(result[0], 180)
+ result[0] = Math.max(result[0], -180)
+ result[1] = Math.min(result[1], 74.000022)
+ result[1] = Math.max(result[1], -71.988531)
+ result = projection.lngLatToPoint({
+ lng: result[0],
+ lat: result[1],
+ })
+ return new Cesium.Cartesian2(result.x, result.y)
+ }
+ this._projection.unproject = function (cartesian, result) {
+ result = result || {}
+ result = projection.mercatorToLngLat({
+ lng: cartesian.x,
+ lat: cartesian.y,
+ })
+ result = CoordTransform.BD09ToGCJ02(result.lng, result.lat)
+ result = CoordTransform.GCJ02ToWGS84(result[0], result[1])
+ return new Cesium.Cartographic(
+ Cesium.Math.toRadians(result[0]),
+ Cesium.Math.toRadians(result[1])
+ )
+ }
+ this.resolutions = options.resolutions || []
+ }
+
+ /**
+ *
+ * @param x
+ * @param y
+ * @param level
+ * @param result
+ * @returns {module:cesium.Rectangle|*}
+ */
+ tileXYToNativeRectangle(x, y, level, result) {
+ const tileWidth = this.resolutions[level]
+ const west = x * tileWidth
+ const east = (x + 1) * tileWidth
+ const north = ((y = -y) + 1) * tileWidth
+ const south = y * tileWidth
+
+ if (!Cesium.defined(result)) {
+ return new Cesium.Rectangle(west, south, east, north)
+ }
+
+ result.west = west
+ result.south = south
+ result.east = east
+ result.north = north
+ return result
+ }
+
+ /**
+ *
+ * @param position
+ * @param level
+ * @param result
+ * @returns {undefined|*}
+ */
+ positionToTileXY(position, level, result) {
+ const rectangle = this._rectangle
+ if (!Cesium.Rectangle.contains(rectangle, position)) {
+ return undefined
+ }
+ const projection = this._projection
+ const webMercatorPosition = projection.project(position)
+ if (!Cesium.defined(webMercatorPosition)) {
+ return undefined
+ }
+ const tileWidth = this.resolutions[level]
+ const xTileCoordinate = Math.floor(webMercatorPosition.x / tileWidth)
+ const yTileCoordinate = -Math.floor(webMercatorPosition.y / tileWidth)
+ if (!Cesium.defined(result)) {
+ return new Cesium.Cartesian2(xTileCoordinate, yTileCoordinate)
+ }
+ result.x = xTileCoordinate
+ result.y = yTileCoordinate
+ return result
+ }
+}
+
+export default BaiduMercatorTilingScheme
diff --git a/src/modules/images/base64/index.js b/src/modules/images/base64/index.js
new file mode 100644
index 00000000..ab710774
--- /dev/null
+++ b/src/modules/images/base64/index.js
@@ -0,0 +1,9 @@
+/**
+ * @Author: Caven
+ * @Date: 2022-05-28 23:30:45
+ */
+
+const IMG_PARTICLES =
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAEACAYAAADSoXR2AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ1IDc5LjE2MzQ5OSwgMjAxOC8wOC8xMy0xNjo0MDoyMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjExQTg0NDEyMDEzQjExRUFBNDhBRjhGMUMzOUUyNTU0IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjExQTg0NDEzMDEzQjExRUFBNDhBRjhGMUMzOUUyNTU0Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MTFBODQ0MTAwMTNCMTFFQUE0OEFGOEYxQzM5RTI1NTQiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MTFBODQ0MTEwMTNCMTFFQUE0OEFGOEYxQzM5RTI1NTQiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz41vRwAAAAE90lEQVR42uydyW4UMRCG3T2dgYSAEGs4sp44cCJBcGUJbwCvALwWPAI8ABwAiUVwgLBdkEikJEiAGMhkZqhfU1aa1sy0g+yaJPyWSupOpPjz0uVyucrJer2eG2fJ3ZgLAQhAgC0PgN8XIlkqgGLE75oih0WmRVZEvop0rHog18rnRe6IzInsthyCXFt+TuSKyGmRXZZDgK5eFrkv8l7kiUhrxN/JSo3pigSvcNmI1bCh3b5LK2+NqHyvyEF9x3z5HgqRRViOAXhW5JrCoNdei/y20gMYxhmRSyIXRY6JTMT4DENLW+SdyD19x/NajDmwmYIW79Hnn+MA4GJEAAIQgABJbMJB+n5Sl9zWZvR9DABUfkJkVt8fi3zUldAEoKl24Y2S1fPZEmBdZFHkkb4vxTLRQ5djbyUf0ncYrD/UADUB8MZno2Q19yx7gIqIAAQgAAEIQIAtYZT+85LvRnjQCoPKR3rQUgN4Y3ZeK30g8qps0qeeAzDn4TWD9+ySG+BBS90DaGnVg9a2tgnR4il9/jkOACoiAhCAAASIZpAAFl6yulO0JACo/IDrn6CedP1zxOciv6wAYNnAP3RVBeWtJQCWzG8iL/X9g4vkqNzMctzUXtintt2KM/aS+bmQaY90rb8CF6tSKiICEIAABCAAAQiwbfcFIWVCbUcc9bZDLKiYAKj4uOv7Bb+ILLj+8W7PCgC7Jhzv44Qdh9yrru8V61jPgd645gD2io+15RiC5ZA5ENNPmGmDmtrta9YAVEQE2FlrQUM/J6efU8cSAD87ojodBaccS6kgiiErGiq/qe93XT+3oGU5BJnbyKrIUs6BQZoQUEcrQ7CYagiGqeKiMgnXLXsg5uJUm/RQJKy8fGS7PMw6SgVQPrJFGZr0kCfUL/7IFjIzrLGpemBQ0kPbehL6I9vMjUh6oEVEAAIQgAAEIAABdpyj0qcK71YrqGUJANNrv+sHOcAiRpDDM1cTY1BEHk7kqyP/2Ac5vLEEQEG8aDnIoTb5ObZRWg5ywG5o1dX4ClNYxT7IISgJPsXGpLut9MCWU0S5bqkmdAa3LQEwcZDAfkZ3tthQfkwNkVeeD6kiuaUabcp6DpS9YyalrAdQ8bT1EFQVUabarND9fNuyB2gREYAABCAAAQhAAAIQoM770UgBXARCIr/koDogVlzgrWuxemBKd0m4JnBed0+5ZQ9gn3BK5LK+P4y5ewoB8DFiKO91COLFfwVuTCZV1hSoYw1APZCsFIkbN1Haaa87wwiKQe6eT27AEX6esPVVd8/kOOdAz/oz9FE0p+uGIKUeqE5C8xgSKiICEIAABCAAAQhAAAIQgAAEIAABCECA6AA+sbkREyDUUYlKD6ggZwzRkr8tewBhusisv+0in5qE/hFchoZTE0TL4p8sTbtIpyahQ4Ag5fKpSVBmfdDECvQTZjoM0U9N6KgkwH9xY7PXpPiCmq5yuaLVldHIO7jgNi5XfOEhCqNh9udHV/RnC5YAUDTftdV4/ivvwEoRVS9XXPWa1FIT5ird8jpSB+BDN3rO8AaGMnGy0I0QRYTvdk6NkOsucuhGCMAgI8Q0isaHbmAevNMhiD4P6iZhstANGiQE2PEGCRo2NcgAsQDwKnxWFdlT17/duWU1BJmuHz6A5bwbEsCScgh+qAHit3Jr1oooOPExdRBLbeJjSpuwuy30AAH+CDAAPH5ltESNYl4AAAAASUVORK5CYII='
+
+export { IMG_PARTICLES }
diff --git a/src/modules/images/circle_blue.png b/src/modules/images/circle_blue.png
new file mode 100644
index 00000000..7576f12a
Binary files /dev/null and b/src/modules/images/circle_blue.png differ
diff --git a/src/modules/images/circle_red.png b/src/modules/images/circle_red.png
new file mode 100644
index 00000000..c4d85265
Binary files /dev/null and b/src/modules/images/circle_red.png differ
diff --git a/src/modules/images/circle_yellow.png b/src/modules/images/circle_yellow.png
new file mode 100644
index 00000000..90835b4e
Binary files /dev/null and b/src/modules/images/circle_yellow.png differ
diff --git a/src/modules/images/cloud.jpg b/src/modules/images/cloud.jpg
new file mode 100644
index 00000000..7fc25eed
Binary files /dev/null and b/src/modules/images/cloud.jpg differ
diff --git a/src/modules/images/fence.png b/src/modules/images/fence.png
new file mode 100644
index 00000000..95798ff6
Binary files /dev/null and b/src/modules/images/fence.png differ
diff --git a/src/modules/images/lighting.png b/src/modules/images/lighting.png
new file mode 100644
index 00000000..b32368ec
Binary files /dev/null and b/src/modules/images/lighting.png differ
diff --git a/src/modules/images/space_line.png b/src/modules/images/space_line.png
new file mode 100644
index 00000000..2251982a
Binary files /dev/null and b/src/modules/images/space_line.png differ
diff --git a/src/modules/index.js b/src/modules/index.js
new file mode 100644
index 00000000..159c250d
--- /dev/null
+++ b/src/modules/index.js
@@ -0,0 +1,6 @@
+/**
+ @author : Caven Chen
+ @date : 2023-05-08
+ */
+
+export { default as Viewer } from './viewer/Viewer.js'
diff --git a/src/modules/layer/Layer.js b/src/modules/layer/Layer.js
new file mode 100644
index 00000000..891769a0
--- /dev/null
+++ b/src/modules/layer/Layer.js
@@ -0,0 +1,350 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-03 09:38:21
+ */
+
+import { Cesium } from '../../namespace'
+import { Util } from '../utils'
+import State from '../state/State'
+import { LayerEventType, OverlayEventType, LayerEvent } from '../event'
+import LayerType from './LayerType'
+
+class Layer {
+ constructor(id) {
+ this._id = Util.uuid()
+ this._bid = id || Util.uuid()
+ this._delegate = undefined
+ this._viewer = undefined
+ this._state = undefined
+ this._show = true
+ this._isGround = false
+ this._cache = {}
+ this._attr = {}
+ this._layerEvent = new LayerEvent()
+ this._layerEvent.on(LayerEventType.ADD, this._onAdd, this)
+ this._layerEvent.on(LayerEventType.REMOVE, this._onRemove, this)
+ }
+
+ get layerId() {
+ return this._id
+ }
+
+ get id() {
+ return this._bid
+ }
+
+ get delegate() {
+ return this._delegate
+ }
+
+ set show(show) {
+ this._show = show
+ this._delegate && (this._delegate.show = this._show)
+ }
+
+ get show() {
+ return this._show
+ }
+
+ get layerEvent() {
+ return this._layerEvent
+ }
+
+ set attr(attr) {
+ this._attr = attr
+ }
+
+ get attr() {
+ return this._attr
+ }
+
+ get state() {
+ return this._state
+ }
+
+ /**
+ * The hook for added
+ * @private
+ */
+ _addedHook() {}
+
+ /**
+ * The hook for removed
+ * @private
+ */
+ _removedHook() {}
+
+ /**
+ * The layer added callback function
+ * Subclasses need to be overridden
+ * @param viewer
+ * @private
+ */
+ _onAdd(viewer) {
+ this._viewer = viewer
+ if (!this._delegate) {
+ return
+ }
+ if (this._delegate instanceof Cesium.PrimitiveCollection) {
+ if (this._isGround) {
+ this._viewer.scene.groundPrimitives.add(this._delegate)
+ } else {
+ this._viewer.scene.primitives.add(this._delegate)
+ }
+ } else {
+ this._viewer.dataSources.add(this._delegate)
+ }
+ this._addedHook && this._addedHook()
+ this._state = State.ADDED
+ }
+
+ /**
+ * The layer added callback function
+ * Subclasses need to be overridden
+ * @private
+ */
+ _onRemove() {
+ if (!this._delegate) {
+ return
+ }
+ if (this._viewer) {
+ this._cache = {}
+ if (this._delegate instanceof Cesium.PrimitiveCollection) {
+ this._delegate.removeAll()
+ if (this._isGround) {
+ this._viewer.scene.groundPrimitives.remove(this._delegate)
+ } else {
+ this._viewer.scene.primitives.remove(this._delegate)
+ }
+ } else if (this._delegate.then) {
+ this._delegate.then((dataSource) => {
+ dataSource.entities.removeAll()
+ })
+ this._viewer.dataSources.remove(this._delegate)
+ } else {
+ this._delegate.entities && this._delegate.entities.removeAll()
+ this._viewer.dataSources.remove(this._delegate)
+ }
+ this._removedHook && this._removedHook()
+ this._state = State.REMOVED
+ }
+ }
+
+ /**
+ * The layer add overlay
+ * @param overlay
+ * @private
+ */
+ _addOverlay(overlay) {
+ // eslint-disable-next-line no-prototype-builtins
+ if (!this._cache.hasOwnProperty(overlay.overlayId)) {
+ this._cache[overlay.overlayId] = overlay
+ this._delegate && overlay.fire(OverlayEventType.ADD, this)
+ if (this._state === State.CLEARED) {
+ this._state = State.ADDED
+ }
+ }
+ }
+
+ /**
+ * The layer remove overlay
+ * @param overlay
+ * @private
+ */
+ _removeOverlay(overlay) {
+ // eslint-disable-next-line no-prototype-builtins
+ if (this._cache.hasOwnProperty(overlay.overlayId)) {
+ this._delegate && overlay.fire(OverlayEventType.REMOVE, this)
+ delete this._cache[overlay.overlayId]
+ }
+ }
+
+ /**
+ * Add overlay
+ * @param overlay
+ * @returns {Layer}
+ */
+ addOverlay(overlay) {
+ this._addOverlay(overlay)
+ return this
+ }
+
+ /**
+ * Add overlays
+ * @param overlays
+ * @returns {Layer}
+ */
+ addOverlays(overlays) {
+ if (Array.isArray(overlays)) {
+ overlays.forEach((item) => {
+ this._addOverlay(item)
+ })
+ }
+ return this
+ }
+
+ /**
+ * Remove overlay
+ * @param overlay
+ * @returns {Layer}
+ */
+ removeOverlay(overlay) {
+ this._removeOverlay(overlay)
+ return this
+ }
+
+ /**
+ * Returns the overlay by overlayId
+ * @param overlayId
+ * @returns {*|undefined}
+ */
+ getOverlay(overlayId) {
+ return this._cache[overlayId] || undefined
+ }
+
+ /**
+ * Returns the overlay by bid
+ * @param id
+ * @returns {any}
+ */
+ getOverlayById(id) {
+ let overlay = undefined
+ Object.keys(this._cache).forEach((key) => {
+ if (this._cache[key].id === id) {
+ overlay = this._cache[key]
+ }
+ })
+ return overlay
+ }
+
+ /**
+ * Returns the overlays by attrName and AttrVal
+ * @param attrName
+ * @param attrVal
+ * @returns {[]}
+ */
+ getOverlaysByAttr(attrName, attrVal) {
+ let result = []
+ this.eachOverlay((item) => {
+ if (item.attr[attrName] === attrVal) {
+ result.push(item)
+ }
+ }, this)
+ return result
+ }
+
+ /**
+ * Iterate through each overlay and pass it as an argument to the callback function
+ * @param method
+ * @param context
+ * @returns {Layer}
+ */
+ eachOverlay(method, context) {
+ Object.keys(this._cache).forEach((key) => {
+ method && method.call(context || this, this._cache[key])
+ })
+ return this
+ }
+
+ /**
+ * Returns all overlays
+ * @returns {[]}
+ */
+ getOverlays() {
+ let result = []
+ Object.keys(this._cache).forEach((key) => {
+ result.push(this._cache[key])
+ })
+ return result
+ }
+
+ /**
+ * Clears all overlays
+ * Subclasses need to be overridden
+ */
+ clear() {}
+
+ /**
+ * Removes from the viewer
+ */
+ remove() {
+ if (this._viewer) {
+ this._viewer.removeLayer(this)
+ }
+ }
+
+ /**
+ * Adds to the viewer
+ * @param viewer
+ * @returns {Layer}
+ */
+ addTo(viewer) {
+ if (viewer?.addLayer) {
+ viewer.addLayer(this)
+ }
+ return this
+ }
+
+ /**
+ * sets the style, the style will apply to every overlay of the layer
+ * Subclasses need to be overridden
+ * @param style
+ */
+ setStyle(style) {}
+
+ /**
+ * Subscribe event
+ * @param type
+ * @param callback
+ * @param context
+ * @returns {Layer}
+ */
+ on(type, callback, context) {
+ this._layerEvent.on(type, callback, context || this)
+ return this
+ }
+
+ /**
+ * Unsubscribe event
+ * @param type
+ * @param callback
+ * @param context
+ * @returns {Layer}
+ */
+ off(type, callback, context) {
+ this._layerEvent.off(type, callback, context || this)
+ return this
+ }
+
+ /**
+ * Trigger subscription event
+ * @param type
+ * @param params
+ * @returns {Layer}
+ */
+ fire(type, params) {
+ this._layerEvent.fire(type, params)
+ return this
+ }
+
+ /**
+ * Registers Type
+ * @param type
+ */
+ static registerType(type) {
+ if (type) {
+ LayerType[type.toLocaleUpperCase()] = type.toLocaleLowerCase()
+ }
+ }
+
+ /**
+ * Returns type
+ * @param type
+ * @returns {*|undefined}
+ */
+ static getLayerType(type) {
+ return LayerType[type.toLocaleUpperCase()] || undefined
+ }
+}
+
+export default Layer
diff --git a/src/modules/layer/LayerGroup.js b/src/modules/layer/LayerGroup.js
new file mode 100644
index 00000000..ced0b895
--- /dev/null
+++ b/src/modules/layer/LayerGroup.js
@@ -0,0 +1,148 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-27 19:50:32
+ */
+
+import { Util } from '../utils'
+import State from '../state/State'
+import { LayerGroupEventType, LayerGroupEvent } from '../event'
+import Layer from './Layer'
+
+class LayerGroup {
+ constructor(id) {
+ this._id = id || Util.uuid()
+ this._cache = {}
+ this._show = true
+ this._viewer = undefined
+ this._layerGroupEvent = new LayerGroupEvent()
+ this._layerGroupEvent.on(LayerGroupEventType.ADD, this._onAdd, this)
+ this._layerGroupEvent.on(LayerGroupEventType.REMOVE, this._onRemove, this)
+ this._state = State.INITIALIZED
+ }
+
+ get id() {
+ return this._id
+ }
+
+ get type() {
+ return Layer.getLayerType('layer_group')
+ }
+
+ set show(show) {
+ this._show = show
+ Object.keys(this._cache).forEach((key) => {
+ this._cache[key].show = this._show
+ })
+ }
+
+ get show() {
+ return this._show
+ }
+
+ get layerGroupEvent() {
+ return this._layerGroupEvent
+ }
+
+ get state() {
+ return this._state
+ }
+
+ /**
+ *
+ * @param viewer
+ * @private
+ */
+ _onAdd(viewer) {
+ this._viewer = viewer
+ Object.keys(this._cache).forEach((key) => {
+ this._viewer.addLayer(this._cache[key])
+ })
+ this._state = State.ADDED
+ }
+
+ /**
+ *
+ * @private
+ */
+ _onRemove() {
+ Object.keys(this._cache).forEach((key) => {
+ this._viewer && this._viewer.removeLayer(this._cache[key])
+ })
+ this._cache = {}
+ this._state = State.REMOVED
+ }
+
+ /**
+ * Adds a layer
+ * @param layer
+ * @returns {LayerGroup}
+ */
+ addLayer(layer) {
+ // eslint-disable-next-line no-prototype-builtins
+ if (!Object(this._cache).hasOwnProperty(layer.id)) {
+ this._cache[layer.id] = layer
+ this._viewer && this._viewer.addLayer(layer)
+ }
+ return this
+ }
+
+ /**
+ * Removes a layer
+ * @param layer
+ * @returns {LayerGroup}
+ */
+ removeLayer(layer) {
+ // eslint-disable-next-line no-prototype-builtins
+ if (Object(this._cache).hasOwnProperty(layer.id)) {
+ this._viewer && this._viewer.removeLayer(layer)
+ delete this._cache[layer.id]
+ }
+ return this
+ }
+
+ /**
+ * Returns a layer by id
+ * @param id
+ * @returns {*|undefined}
+ */
+ getLayer(id) {
+ return this._cache[id] || undefined
+ }
+
+ /**
+ * Returns all layers
+ * @returns {[]}
+ */
+ getLayers() {
+ let result = []
+ Object.keys(this._cache).forEach((key) => {
+ result.push(this._cache[key])
+ })
+ return result
+ }
+
+ /**
+ * Adds to the viewer
+ * @param viewer
+ * @returns {LayerGroup}
+ */
+ addTo(viewer) {
+ if (viewer && viewer.addLayerGroup) {
+ viewer.addLayerGroup(this)
+ }
+ return this
+ }
+
+ /**
+ *
+ * @returns {LayerGroup}
+ */
+ remove() {
+ this._viewer && this._viewer.removeLayerGroup(this)
+ return this
+ }
+}
+
+Layer.registerType('layer_group')
+
+export default LayerGroup
diff --git a/src/modules/layer/LayerType.js b/src/modules/layer/LayerType.js
new file mode 100644
index 00000000..e807a256
--- /dev/null
+++ b/src/modules/layer/LayerType.js
@@ -0,0 +1,8 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-05-10 08:21:19
+ */
+
+let LayerType = {}
+
+export default LayerType
diff --git a/src/modules/layer/index.js b/src/modules/layer/index.js
new file mode 100644
index 00000000..e24791ab
--- /dev/null
+++ b/src/modules/layer/index.js
@@ -0,0 +1,27 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-03-13 13:46:19
+ */
+
+export { default as LayerType } from './LayerType'
+export { default as Layer } from './Layer'
+export { default as LayerGroup } from './LayerGroup'
+
+/**
+ * types
+ */
+export { default as ClusterLayer } from './type/ClusterLayer'
+export { default as CzmlLayer } from './type/CzmlLayer'
+export { default as DynamicLayer } from './type/DynamicLayer'
+export { default as FeatureGridLayer } from './type/FeatureGridLayer'
+export { default as GeoJsonLayer } from './type/GeoJsonLayer'
+export { default as GpxLayer } from './type/GpxLayer'
+export { default as GraticuleLayer } from './type/GraticuleLayer'
+export { default as GroundPrimitiveLayer } from './type/GroundPrimitiveLayer'
+export { default as HtmlLayer } from './type/HtmlLayer'
+export { default as KmlLayer } from './type/KmlLayer'
+export { default as LabelLayer } from './type/LabelLayer'
+export { default as PrimitiveLayer } from './type/PrimitiveLayer'
+export { default as TilesetLayer } from './type/TilesetLayer'
+export { default as TopoJsonLayer } from './type/TopoJsonLayer'
+export { default as VectorLayer } from './type/VectorLayer'
diff --git a/src/modules/layer/type/ClusterLayer.js b/src/modules/layer/type/ClusterLayer.js
new file mode 100644
index 00000000..9567db28
--- /dev/null
+++ b/src/modules/layer/type/ClusterLayer.js
@@ -0,0 +1,183 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-02-10 10:05:41
+ */
+
+import { Cesium } from '../../../namespace'
+import State from '../../state/State'
+import Layer from '../Layer'
+
+const DEF_OPT = {
+ size: 18,
+ pixelRange: 40,
+ gradient: {
+ 0.0001: Cesium.Color.DEEPSKYBLUE,
+ 0.001: Cesium.Color.GREEN,
+ 0.01: Cesium.Color.ORANGE,
+ 0.1: Cesium.Color.RED,
+ },
+ fontSize: 12,
+ fontColor: Cesium.Color.BLACK,
+ style: 'circle',
+}
+
+class ClusterLayer extends Layer {
+ constructor(id, options = {}) {
+ super(id)
+ this._delegate = new Cesium.CustomDataSource(id)
+ this._options = {
+ ...DEF_OPT,
+ ...options,
+ }
+ this._delegate.clustering.enabled = true
+ this._delegate.clustering.clusterEvent.addEventListener(
+ this._clusterEventHandler,
+ this
+ )
+ this._delegate.clustering.pixelRange = this._options.pixelRange
+
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Layer.getLayerType('cluster')
+ }
+
+ set enableCluster(enableCluster) {
+ this._delegate.clustering.enabled = enableCluster
+ }
+
+ /**
+ *
+ * @param color
+ * @param numLength
+ * @returns {*}
+ * @private
+ */
+ _drawCircle(color, numLength) {
+ let size = this._options.size * (numLength + 1)
+ let key = color.toCssColorString() + '-' + size
+ if (!this._cache[key]) {
+ let canvas = document.createElement('canvas')
+ canvas.width = size
+ canvas.height = size
+ let context2D = canvas.getContext('2d')
+ context2D.save()
+ context2D.scale(size / 24, size / 24) //Added to auto-generated code to scale up to desired size.
+ context2D.fillStyle = color.withAlpha(0.2).toCssColorString() //Modified from auto-generated code.
+ context2D.beginPath()
+ context2D.arc(12, 12, 9, 0, 2 * Math.PI)
+ context2D.closePath()
+ context2D.fill()
+ context2D.beginPath()
+ context2D.arc(12, 12, 6, 0, 2 * Math.PI)
+ context2D.fillStyle = color.toCssColorString()
+ context2D.fill()
+ context2D.closePath()
+ context2D.restore()
+ this._cache[key] = canvas.toDataURL()
+ }
+ return this._cache[key]
+ }
+
+ /**
+ *
+ * @param color
+ * @param numLength
+ * @returns {*}
+ * @private
+ */
+ _drawClustering(color, numLength) {
+ let size = this._options.size * (numLength + 1)
+ let key = color.toCssColorString() + '-' + size
+ let startAngle = -Math.PI / 12
+ let angle = Math.PI / 2
+ let intervalAngle = Math.PI / 6
+ if (!this._cache[key]) {
+ let canvas = document.createElement('canvas')
+ canvas.width = size
+ canvas.height = size
+ let context2D = canvas.getContext('2d')
+ context2D.save()
+ context2D.scale(size / 24, size / 24) //Added to auto-generated code to scale up to desired size.
+ context2D.beginPath()
+ context2D.arc(12, 12, 6, 0, 2 * Math.PI)
+ context2D.fillStyle = color.toCssColorString()
+ context2D.fill()
+ context2D.closePath()
+ context2D.lineWidth = 2
+ for (let i = 0; i < 3; i++) {
+ context2D.beginPath()
+ context2D.arc(12, 12, 8, startAngle, startAngle + angle, false)
+ context2D.strokeStyle = color.withAlpha(0.4).toCssColorString()
+ context2D.stroke()
+ context2D.arc(12, 12, 11, startAngle, startAngle + angle, false)
+ context2D.strokeStyle = color.withAlpha(0.2).toCssColorString()
+ context2D.stroke()
+ context2D.closePath()
+ startAngle = startAngle + angle + intervalAngle
+ }
+ context2D.restore()
+ this._cache[key] = canvas.toDataURL()
+ }
+ return this._cache[key]
+ }
+
+ /**
+ *
+ * @param {*} clusteredEntities
+ * @param {*} cluster
+ */
+ _clusterEventHandler(clusteredEntities, cluster) {
+ if (!this._delegate.clustering.enabled) {
+ return
+ }
+ cluster.billboard.show = true
+ cluster.label.font = `bold ${this._options.fontSize}px sans-serif`
+ cluster.label.fillColor = this._options.fontColor
+ cluster.label.disableDepthTestDistance = Number.POSITIVE_INFINITY
+ if (this._delegate.entities.values.length) {
+ let allCount = this._delegate.entities.values.length || 0
+ for (let key in this._options.gradient) {
+ if (clusteredEntities.length >= allCount * key) {
+ let numLength = String(clusteredEntities.length).length
+ if (this._options.style === 'circle') {
+ cluster.billboard.image = this._drawCircle(
+ this._options.gradient[key],
+ numLength
+ )
+ } else if (this._options.style === 'custom') {
+ cluster.billboard.image = this._options.gradient[key]
+ } else {
+ cluster.billboard.image = this._drawClustering(
+ this._options.gradient[key],
+ numLength
+ )
+ }
+ cluster.label.show = true
+ if (numLength === 1) {
+ cluster.label.pixelOffset = new Cesium.Cartesian2(-2, 3)
+ } else {
+ cluster.label.pixelOffset = new Cesium.Cartesian2(
+ -5 * (numLength - 1),
+ 5
+ )
+ }
+ } else if (clusteredEntities.length <= 1) {
+ cluster.label.show = false
+ }
+ }
+ }
+ }
+
+ clear() {
+ this._delegate.entities.removeAll()
+ this._cache = {}
+ this._state = State.CLEARED
+ return this
+ }
+}
+
+Layer.registerType('cluster')
+
+export default ClusterLayer
diff --git a/src/modules/layer/type/CzmlLayer.js b/src/modules/layer/type/CzmlLayer.js
new file mode 100644
index 00000000..3fe04cb3
--- /dev/null
+++ b/src/modules/layer/type/CzmlLayer.js
@@ -0,0 +1,54 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-19 13:38:48
+ */
+
+import { Cesium } from '../../../namespace'
+import State from '../../state/State'
+import Layer from '../Layer'
+
+class CzmlLayer extends Layer {
+ constructor(id, url = '', options = {}) {
+ super(id)
+ this._delegate = Cesium.CzmlDataSource.load(url, options)
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Layer.getLayerType('czml')
+ }
+
+ set show(show) {
+ this._show = show
+ this._delegate &&
+ this._delegate.then((dataSource) => {
+ dataSource.show = this._show
+ })
+ }
+
+ get show() {
+ return this._show
+ }
+
+ /**
+ *
+ * @param method
+ * @param context
+ * @returns {CzmlLayer}
+ */
+ eachOverlay(method, context) {
+ if (this._delegate) {
+ this._delegate.then((dataSource) => {
+ let entities = dataSource.entities.values
+ entities.forEach((item) => {
+ method.call(context, item)
+ })
+ })
+ return this
+ }
+ }
+}
+
+Layer.registerType('czml')
+
+export default CzmlLayer
diff --git a/src/modules/layer/type/DynamicLayer.js b/src/modules/layer/type/DynamicLayer.js
new file mode 100644
index 00000000..54c8ff6f
--- /dev/null
+++ b/src/modules/layer/type/DynamicLayer.js
@@ -0,0 +1,35 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-05-05 09:12:41
+ */
+
+import { Cesium } from '../../../namespace'
+import State from '../../state/State'
+import Layer from '../Layer'
+
+class DynamicLayer extends Layer {
+ constructor(id) {
+ super(id)
+ this._delegate = new Cesium.CustomDataSource(id)
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Layer.getLayerType('dynamic')
+ }
+
+ /**
+ * Clears all entities
+ * @returns {DynamicLayer}
+ */
+ clear() {
+ this._delegate.entities && this._delegate.entities.removeAll()
+ this._cache = {}
+ this._state = State.CLEARED
+ return this
+ }
+}
+
+Layer.registerType('dynamic')
+
+export default DynamicLayer
diff --git a/src/modules/layer/type/FeatureGridLayer.js b/src/modules/layer/type/FeatureGridLayer.js
new file mode 100644
index 00000000..4f139884
--- /dev/null
+++ b/src/modules/layer/type/FeatureGridLayer.js
@@ -0,0 +1,221 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-04-15 20:00:42
+ */
+
+import { Cesium } from '../../../namespace'
+import State from '../../state/State'
+import Layer from '../Layer'
+import VectorLayer from './VectorLayer'
+
+const DEF_OPTS = {
+ name: '',
+ count: 10,
+ maximumLevel: 21,
+ dataProperty: '',
+ callback: () => {
+ return null
+ },
+}
+
+class FeatureGridLayer extends Layer {
+ constructor(id, url, options = {}) {
+ super(id)
+ this._url = url
+ this._options = {
+ ...DEF_OPTS,
+ ...options,
+ }
+ this._levelLayers = {}
+ this._tileWidth = 256
+ this._tileHeight = 256
+ this._maximumLevel = this._options.maximumLevel
+ this._tilingScheme =
+ this._options.tilingScheme || new Cesium.GeographicTilingScheme()
+ this._rectangle = this._tilingScheme.rectangle
+ this._credit = undefined
+ this._token = undefined
+ for (let i = 0; i < this.maximumLevel; i++) {
+ this._levelLayers[String(i)] = new VectorLayer(id + '-grid-' + i)
+ }
+ this._viewer = undefined
+ this._imageryLayer = undefined
+ this._imagery = document.createElement('canvas')
+ this._imagery.width = this._tileWidth
+ this._imagery.height = this._tileHeight
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Layer.getLayerType('feature_grid')
+ }
+
+ get url() {
+ return this._url
+ }
+
+ set show(show) {
+ this._show = show
+ Object.keys(this._levelLayers).forEach((key) => {
+ this._levelLayers[key].show = show
+ })
+ return this
+ }
+
+ get show() {
+ return this._show
+ }
+
+ get token() {
+ return this._token
+ }
+
+ get tileWidth() {
+ if (!this.ready) {
+ throw new Cesium.DeveloperError(
+ 'tileWidth must not be called before the imagery provider is ready.'
+ )
+ }
+ return this._tileWidth
+ }
+
+ get tileHeight() {
+ if (!this.ready) {
+ throw new Cesium.DeveloperError(
+ 'tileHeight must not be called before the imagery provider is ready.'
+ )
+ }
+ return this._tileHeight
+ }
+
+ get maximumLevel() {
+ if (!this.ready) {
+ throw new Cesium.DeveloperError(
+ 'maximumLevel must not be called before the imagery provider is ready.'
+ )
+ }
+ return this._maximumLevel
+ }
+
+ get minimumLevel() {
+ if (!this.ready) {
+ throw new Cesium.DeveloperError(
+ 'minimumLevel must not be called before the imagery provider is ready.'
+ )
+ }
+ return 0
+ }
+
+ get tilingScheme() {
+ if (!this.ready) {
+ throw new Cesium.DeveloperError(
+ 'tilingScheme must not be called before the imagery provider is ready.'
+ )
+ }
+ return this._tilingScheme
+ }
+
+ get rectangle() {
+ if (!this.ready) {
+ throw new Cesium.DeveloperError(
+ 'rectangle must not be called before the imagery provider is ready.'
+ )
+ }
+ return this._rectangle
+ }
+
+ get ready() {
+ return !!this._url
+ }
+
+ get credit() {
+ return this._credit
+ }
+
+ get hasAlphaChannel() {
+ return true
+ }
+
+ /**
+ *
+ * @param {*} viewer
+ * @returns
+ */
+ _onAdd(viewer) {
+ this._viewer = viewer
+ this._imageryLayer = this._viewer.imageryLayers.addImageryProvider(this)
+ Object.keys(this._levelLayers).forEach((key) => {
+ this._viewer.addLayer(this._levelLayers[key])
+ })
+ this._state = State.ADDED
+ }
+
+ _onRemove() {
+ this._imageryLayer && this._viewer.imageryLayers.remove(this._imageryLayer)
+ Object.keys(this._levelLayers).forEach((key) => {
+ this._viewer.removeLayer(this._levelLayers[key])
+ })
+ this._state = State.REMOVED
+ }
+
+ getTileCredits(x, y, level) {}
+
+ /**
+ *
+ * @param {*} x
+ * @param {*} y
+ * @param {*} level
+ * @param {*} request
+ * @returns
+ */
+ requestImage(x, y, level, request) {
+ let layer = this._levelLayers[String(level)]
+ let rectangle = this._tilingScheme.tileXYToRectangle(x, y, level)
+ if (
+ this._viewer &&
+ rectangle &&
+ layer &&
+ Cesium.Rectangle.intersection(rectangle, this._viewer.viewBounds)
+ ) {
+ Cesium.Resource.fetchJson({
+ url: this._url,
+ queryParameters: {
+ minX: Cesium.Math.toDegrees(rectangle.west),
+ minY: Cesium.Math.toDegrees(rectangle.south),
+ maxX: Cesium.Math.toDegrees(rectangle.east),
+ maxY: Cesium.Math.toDegrees(rectangle.north),
+ count: this._options.count,
+ },
+ }).then((res) => {
+ let dataList = res
+ if (this._options.dataProperty) {
+ dataList = res[this._options.dataProperty]
+ }
+ if (dataList && dataList.length) {
+ for (let i = level + 3; i < this._maximumLevel; i++) {
+ this._levelLayers[String(i)] && this._levelLayers[String(i)].clear()
+ }
+ dataList.forEach((item) => {
+ let overlay = this._options.callback(item)
+ overlay && layer.addOverlay(overlay)
+ })
+ }
+ })
+ }
+ return this._imagery
+ }
+
+ /**
+ *
+ */
+ clear() {
+ Object.keys(this._levelLayers).forEach((key) => {
+ this._levelLayers[key].clear()
+ })
+ this._state = State.CLEARED
+ }
+}
+
+Layer.registerType('feature_grid')
+
+export default FeatureGridLayer
diff --git a/src/modules/layer/type/GeoJsonLayer.js b/src/modules/layer/type/GeoJsonLayer.js
new file mode 100644
index 00000000..1daacaf5
--- /dev/null
+++ b/src/modules/layer/type/GeoJsonLayer.js
@@ -0,0 +1,133 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-13 10:13:53
+ */
+
+import { Cesium } from '../../../namespace'
+import State from '../../state/State'
+import { Billboard, Polyline, Polygon, Model } from '../../overlay'
+import Layer from '../Layer'
+import VectorLayer from './VectorLayer'
+
+class GeoJsonLayer extends Layer {
+ constructor(id, url, options = {}) {
+ if (!url) {
+ throw new Error('GeoJsonLayer:the url invalid')
+ }
+ super(id)
+ this._delegate = Cesium.GeoJsonDataSource.load(url, options)
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Layer.getLayerType('geojson')
+ }
+
+ set show(show) {
+ this._show = show
+ this._delegate &&
+ this._delegate.then((dataSource) => {
+ dataSource.show = this._show
+ })
+ }
+
+ get show() {
+ return this._show
+ }
+
+ _createBillboard(entity) {
+ if (entity.position && entity.billboard) {
+ return Billboard.fromEntity(entity)
+ }
+ }
+
+ /**
+ * Returns polyline Entity
+ * @param entity
+ * @returns {any}
+ * @private
+ */
+ _createPolyline(entity) {
+ if (entity.polyline) {
+ return Polyline.fromEntity(entity)
+ }
+ }
+
+ /**
+ * Returns polygon Entity
+ * @param entity
+ * @returns {any}
+ * @private
+ */
+ _createPolygon(entity) {
+ if (entity.polygon) {
+ return Polygon.fromEntity(entity)
+ }
+ }
+
+ /**
+ * Returns model Entity
+ * @param entity
+ * @param modelUrl
+ * @returns {Model}
+ * @private
+ */
+ _createModel(entity, modelUrl) {
+ if (entity) {
+ return Model.fromEntity(entity, modelUrl)
+ }
+ }
+
+ /**
+ *
+ * @param method
+ * @param context
+ * @returns {GeoJsonLayer}
+ */
+ eachOverlay(method, context) {
+ if (this._delegate) {
+ this._delegate.then((dataSource) => {
+ let entities = dataSource.entities.values
+ entities.forEach((item) => {
+ method.call(context, item)
+ })
+ })
+ return this
+ }
+ }
+
+ /**
+ * Converts to VectorLayer
+ * @returns {VectorLayer}
+ */
+ toVectorLayer() {
+ let layer = new VectorLayer(this.id)
+ this.eachOverlay((item) => {
+ if (item.billboard) {
+ layer.addOverlay(this._createBillboard(item))
+ } else if (item.polyline) {
+ layer.addOverlay(this._createPolyline(item))
+ } else if (item.polygon) {
+ layer.addOverlay(this._createPolygon(item))
+ }
+ }, this)
+ return layer
+ }
+
+ /**
+ * Converts to VectorLayer
+ * @param modelUrl
+ * @returns {VectorLayer}
+ */
+ toModelLayer(modelUrl) {
+ let layer = new VectorLayer(this.id)
+ this.eachOverlay((item) => {
+ layer.addOverlay(this._createModel(item, modelUrl))
+ }, this)
+ return layer
+ }
+}
+
+Layer.registerType('geojson')
+
+export default GeoJsonLayer
diff --git a/src/modules/layer/type/GpxLayer.js b/src/modules/layer/type/GpxLayer.js
new file mode 100644
index 00000000..b9ba64db
--- /dev/null
+++ b/src/modules/layer/type/GpxLayer.js
@@ -0,0 +1,51 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-19 11:03:17
+ */
+
+import { Cesium } from '../../../namespace'
+import State from '../../state/State'
+import Layer from '../Layer'
+
+class GpxLayer extends Layer {
+ constructor(id, url, options = {}) {
+ if (!url) {
+ throw new Error('GpxLayer: the url is empty')
+ }
+ super(id)
+ this._delegate = Cesium.KmlDataSource.load(url, options)
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Layer.getLayerType('gpx')
+ }
+
+ set show(show) {
+ this._show = show
+ this._delegate &&
+ this._delegate.then((dataSource) => {
+ dataSource.show = this._show
+ })
+ }
+
+ get show() {
+ return this._show
+ }
+
+ eachOverlay(method, context) {
+ if (this._delegate) {
+ this._delegate.then((dataSource) => {
+ let entities = dataSource.entities.values
+ entities.forEach((item) => {
+ method.call(context, item)
+ })
+ })
+ return this
+ }
+ }
+}
+
+Layer.registerType('gpx')
+
+export default GpxLayer
diff --git a/src/modules/layer/type/GraticuleLayer.js b/src/modules/layer/type/GraticuleLayer.js
new file mode 100644
index 00000000..a05a198c
--- /dev/null
+++ b/src/modules/layer/type/GraticuleLayer.js
@@ -0,0 +1,78 @@
+/**
+ @Author: Caven Chen
+ @Date: 2023-01-12
+**/
+
+import { Cesium } from '../../../namespace'
+import State from '../../state/State'
+import Layer from '../Layer'
+
+const DEF_OPTS = {
+ radialColor: Cesium.Color.WHITE,
+ radialWidth: 2,
+ showRadial: true,
+ LabelColor: Cesium.Color.YELLOW,
+ weftColor: Cesium.Color.WHITE,
+ weftWidth: 2,
+}
+
+class GraticuleLayer extends Layer {
+ constructor(id, options) {
+ super(id)
+ this._options = {
+ ...DEF_OPTS,
+ ...options,
+ }
+ this._delegate = new Cesium.CustomDataSource(id)
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Layer.getLayerType('graticule')
+ }
+
+ _addedHook() {
+ for (let i = 0; i < 36; i++) {
+ this._delegate.entities.add({
+ polyline: {
+ positions: Cesium.Cartesian3.fromDegreesArray([
+ i * 10,
+ 89.5,
+ i * 10,
+ -89.5,
+ ]),
+ material: this._options.radialColor,
+ width: this._options.radialWidth,
+ },
+ show: this._options.showRadial,
+ })
+
+ this._delegate.entities.add({
+ position: Cesium.Cartesian3.fromDegrees(i * 10, 0.0),
+ label: {
+ text: i * 10 + '°',
+ font: '12px',
+ fillColor: this._options.LabelColor,
+ },
+ })
+ }
+
+ for (let i = 0; i < 18; i++) {
+ let coords = []
+ for (let k = 0; k < 129; k++) {
+ coords.push(-180 + k * (360 / 128), (i < 9 ? i : -i) * 10)
+ }
+ this._delegate.entities.add({
+ polyline: {
+ positions: Cesium.Cartesian3.fromDegreesArray(coords),
+ material: this._options.weftColor,
+ width: this._options.weftWidth,
+ },
+ })
+ }
+ }
+}
+
+Layer.registerType('graticule')
+
+export default GraticuleLayer
diff --git a/src/modules/layer/type/GroundPrimitiveLayer.js b/src/modules/layer/type/GroundPrimitiveLayer.js
new file mode 100644
index 00000000..24dabc88
--- /dev/null
+++ b/src/modules/layer/type/GroundPrimitiveLayer.js
@@ -0,0 +1,36 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-12-03 20:12:59
+ */
+
+import { Cesium } from '../../../namespace'
+import State from '../../state/State'
+import Layer from '../Layer'
+
+class GroundPrimitiveLayer extends Layer {
+ constructor(id) {
+ super(id)
+ this._delegate = new Cesium.PrimitiveCollection()
+ this._isGround = true
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Layer.getLayerType('ground_primitive')
+ }
+
+ /**
+ *
+ * @return {GroundPrimitiveLayer}
+ */
+ clear() {
+ this._delegate && this._delegate.removeAll()
+ this._cache = {}
+ this._state = State.CLEARED
+ return this
+ }
+}
+
+Layer.registerType('ground_primitive')
+
+export default GroundPrimitiveLayer
diff --git a/src/modules/layer/type/HtmlLayer.js b/src/modules/layer/type/HtmlLayer.js
new file mode 100644
index 00000000..a0191862
--- /dev/null
+++ b/src/modules/layer/type/HtmlLayer.js
@@ -0,0 +1,98 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-02-12 21:43:33
+ */
+
+import { Cesium } from '../../../namespace'
+import State from '../../state/State'
+import { DomUtil } from '../../utils'
+import { Transform } from '../../transform'
+import Layer from '../Layer'
+
+class HtmlLayer extends Layer {
+ constructor(id) {
+ super(id)
+ this._delegate = DomUtil.create('div', 'html-layer', undefined)
+ this._delegate.setAttribute('id', this._id)
+ this._renderRemoveCallback = undefined
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Layer.getLayerType('html')
+ }
+
+ set show(show) {
+ this._show = show
+ this._delegate.style.visibility = this._show ? 'visible' : 'hidden'
+ Object.keys(this._cache).forEach((key) => {
+ this._cache[key].show = show
+ })
+ }
+
+ get show() {
+ return this._show
+ }
+
+ /**
+ * add handler
+ * @param viewer
+ * @private
+ */
+ _onAdd(viewer) {
+ this._viewer = viewer
+ this._viewer.dcContainer.appendChild(this._delegate)
+ let scene = this._viewer.scene
+ this._renderRemoveCallback = scene.postRender.addEventListener(() => {
+ let cp = this._viewer.camera.positionWC
+ let cd = this._viewer.camera.direction
+ this.eachOverlay((item) => {
+ if (item && item.position) {
+ let position = Transform.transformWGS84ToCartesian(item.position)
+ let up = scene.globe.ellipsoid.geodeticSurfaceNormal(
+ position,
+ new Cesium.Cartesian3()
+ )
+ let windowCoord = Cesium.SceneTransforms.wgs84ToWindowCoordinates(
+ scene,
+ position
+ )
+ item._updateStyle(
+ windowCoord,
+ Cesium.Cartesian3.distance(position, cp),
+ Cesium.Cartesian3.dot(cd, up) <= 0
+ )
+ }
+ }, this)
+ }, this)
+ this._state = State.ADDED
+ }
+
+ /**
+ * remove handler
+ * @returns {boolean}
+ * @private
+ */
+ _onRemove() {
+ this._renderRemoveCallback && this._renderRemoveCallback()
+ this._viewer.dcContainer.removeChild(this._delegate)
+ this._state = State.REMOVED
+ }
+
+ /**
+ * Clears all divIcons
+ * @returns {HtmlLayer}
+ */
+ clear() {
+ while (this._delegate.hasChildNodes()) {
+ this._delegate.removeChild(this._delegate.firstChild)
+ }
+ this._cache = {}
+ this._state = State.CLEARED
+ return this
+ }
+}
+
+Layer.registerType('html')
+
+export default HtmlLayer
diff --git a/src/modules/layer/type/KmlLayer.js b/src/modules/layer/type/KmlLayer.js
new file mode 100644
index 00000000..a26c201a
--- /dev/null
+++ b/src/modules/layer/type/KmlLayer.js
@@ -0,0 +1,51 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-19 11:03:17
+ */
+
+import { Cesium } from '../../../namespace'
+import State from '../../state/State'
+import Layer from '../Layer'
+
+class KmlLayer extends Layer {
+ constructor(id, url, options = {}) {
+ if (!url) {
+ throw new Error('KmlLayer: the url is empty')
+ }
+ super(id)
+ this._delegate = Cesium.KmlDataSource.load(url, options)
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Layer.getLayerType('kml')
+ }
+
+ set show(show) {
+ this._show = show
+ this._delegate &&
+ this._delegate.then((dataSource) => {
+ dataSource.show = this._show
+ })
+ }
+
+ get show() {
+ return this._show
+ }
+
+ eachOverlay(method, context) {
+ if (this._delegate) {
+ this._delegate.then((dataSource) => {
+ let entities = dataSource.entities.values
+ entities.forEach((item) => {
+ method.call(context, item)
+ })
+ })
+ return this
+ }
+ }
+}
+
+Layer.registerType('kml')
+
+export default KmlLayer
diff --git a/src/modules/layer/type/LabelLayer.js b/src/modules/layer/type/LabelLayer.js
new file mode 100644
index 00000000..6542a87f
--- /dev/null
+++ b/src/modules/layer/type/LabelLayer.js
@@ -0,0 +1,43 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-03-30 17:14:00
+ */
+
+import { Cesium } from '../../../namespace'
+import State from '../../state/State'
+import { Label } from '../../overlay'
+import Layer from '../Layer'
+
+class LabelLayer extends Layer {
+ constructor(id, url = '') {
+ super(id)
+ this._dataSource = Cesium.GeoJsonDataSource.load(url)
+ this._delegate = new Cesium.CustomDataSource(id)
+ this._initLabel()
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Layer.getLayerType('label')
+ }
+
+ _createLabel(entity) {
+ if (entity.position && entity.name) {
+ return Label.fromEntity(entity)
+ }
+ }
+
+ _initLabel() {
+ this._dataSource.then((dataSource) => {
+ let entities = dataSource.entities.values
+ entities.forEach((item) => {
+ let label = this._createLabel(item)
+ this.addOverlay(label)
+ })
+ })
+ }
+}
+
+Layer.registerType('label')
+
+export default LabelLayer
diff --git a/src/modules/layer/type/PrimitiveLayer.js b/src/modules/layer/type/PrimitiveLayer.js
new file mode 100644
index 00000000..659dbc43
--- /dev/null
+++ b/src/modules/layer/type/PrimitiveLayer.js
@@ -0,0 +1,72 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-10-11 18:16:47
+ */
+
+import { Cesium } from '../../../namespace'
+import State from '../../state/State'
+import Layer from '../Layer'
+
+/**
+ * PrimitiveLayer is used to add primitive
+ */
+class PrimitiveLayer extends Layer {
+ constructor(id) {
+ super(id)
+ this._delegate = new Cesium.PrimitiveCollection()
+ this._points = this._delegate.add(new Cesium.PointPrimitiveCollection())
+ this._labels = this._delegate.add(new Cesium.LabelCollection())
+ this._billboards = this._delegate.add(new Cesium.BillboardCollection())
+ this._polylines = this._delegate.add(new Cesium.PolylineCollection())
+ if (Cesium.CloudCollection) {
+ this._clouds = this._delegate.add(new Cesium.CloudCollection())
+ }
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Layer.getLayerType('primitive')
+ }
+
+ get points() {
+ return this._points
+ }
+
+ get labels() {
+ return this._labels
+ }
+
+ get billboards() {
+ return this._billboards
+ }
+
+ get polylines() {
+ return this._polylines
+ }
+
+ get clouds() {
+ return this._clouds
+ }
+
+ /**
+ * Clears all primitives
+ * @returns {PrimitiveLayer}
+ */
+ clear() {
+ this._delegate && this._delegate.removeAll()
+ this._points = this._delegate.add(new Cesium.PointPrimitiveCollection())
+ this._labels = this._delegate.add(new Cesium.LabelCollection())
+ this._billboards = this._delegate.add(new Cesium.BillboardCollection())
+ this._polylines = this._delegate.add(new Cesium.PolylineCollection())
+ if (Cesium.CloudCollection) {
+ this._clouds = this._delegate.add(new Cesium.CloudCollection())
+ }
+ this._cache = {}
+ this._state = State.CLEARED
+ return this
+ }
+}
+
+Layer.registerType('primitive')
+
+export default PrimitiveLayer
diff --git a/src/modules/layer/type/TilesetLayer.js b/src/modules/layer/type/TilesetLayer.js
new file mode 100644
index 00000000..99c96596
--- /dev/null
+++ b/src/modules/layer/type/TilesetLayer.js
@@ -0,0 +1,38 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-09 09:16:27
+ */
+
+import { Cesium } from '../../../namespace'
+import State from '../../state/State'
+import Layer from '../Layer'
+
+/**
+ * TilesetLayer is used to add various tileset
+ */
+class TilesetLayer extends Layer {
+ constructor(id) {
+ super(id)
+ this._delegate = new Cesium.PrimitiveCollection()
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Layer.getLayerType('tileset')
+ }
+
+ /**
+ * Clear all tileset
+ * @returns {TilesetLayer}
+ */
+ clear() {
+ this._delegate.removeAll()
+ this._cache = {}
+ this._state = State.CLEARED
+ return this
+ }
+}
+
+Layer.registerType('tileset')
+
+export default TilesetLayer
diff --git a/src/modules/layer/type/TopoJsonLayer.js b/src/modules/layer/type/TopoJsonLayer.js
new file mode 100644
index 00000000..bf701b0e
--- /dev/null
+++ b/src/modules/layer/type/TopoJsonLayer.js
@@ -0,0 +1,26 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-09-11 19:32:22
+ */
+
+import State from '../../state/State'
+import GeoJsonLayer from './GeoJsonLayer'
+import Layer from '../Layer'
+
+class TopoJsonLayer extends GeoJsonLayer {
+ constructor(id, url, options = {}) {
+ if (!url) {
+ throw new Error('TopoJsonLayer:the url invalid')
+ }
+ super(id, url, options)
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Layer.getLayerType('topojson')
+ }
+}
+
+GeoJsonLayer.registerType('topojson')
+
+export default TopoJsonLayer
diff --git a/src/modules/layer/type/VectorLayer.js b/src/modules/layer/type/VectorLayer.js
new file mode 100644
index 00000000..3631a3f0
--- /dev/null
+++ b/src/modules/layer/type/VectorLayer.js
@@ -0,0 +1,39 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-02 16:42:03
+ */
+
+import { Cesium } from '../../../namespace'
+import State from '../../state/State'
+import Layer from '../Layer'
+
+/**
+ * The vector layer is used to add various entity, which is essentially a CustomDataSource
+ * that is used to place entities of the same class or business attribute into the same layer
+ */
+class VectorLayer extends Layer {
+ constructor(id) {
+ super(id)
+ this._delegate = new Cesium.CustomDataSource(id)
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Layer.getLayerType('vector')
+ }
+
+ /**
+ * Clears all entities
+ * @returns {VectorLayer}
+ */
+ clear() {
+ this._delegate.entities && this._delegate.entities.removeAll()
+ this._cache = {}
+ this._state = State.CLEARED
+ return this
+ }
+}
+
+Layer.registerType('vector')
+
+export default VectorLayer
diff --git a/src/modules/material/MaterialProperty.js b/src/modules/material/MaterialProperty.js
new file mode 100644
index 00000000..d3bd12ff
--- /dev/null
+++ b/src/modules/material/MaterialProperty.js
@@ -0,0 +1,41 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-02-27 22:33:50
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+
+class MaterialProperty {
+ constructor(options = {}) {
+ this._definitionChanged = new Cesium.Event()
+ this._color = undefined
+ this._colorSubscription = undefined
+ this._speed = undefined
+ this._speedSubscription = undefined
+ this.color = options.color || Cesium.Color.fromBytes(0, 255, 255, 255)
+ this.speed = options.speed || 1
+ }
+
+ get isConstant() {
+ return false
+ }
+
+ get definitionChanged() {
+ return this._definitionChanged
+ }
+
+ getType(time) {
+ return null
+ }
+
+ getValue(time, result) {
+ result = Cesium.defaultValue(result, {})
+ return result
+ }
+
+ equals(other) {
+ return this === other
+ }
+}
+
+export default MaterialProperty
diff --git a/src/modules/material/index.js b/src/modules/material/index.js
new file mode 100644
index 00000000..d2c4c6dd
--- /dev/null
+++ b/src/modules/material/index.js
@@ -0,0 +1,63 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-02-24 13:28:37
+ */
+
+// material
+
+export * from './type/thirdpart'
+export * from './type/circle'
+export * from './type/cylinder'
+export * from './type/ellipsoid'
+export * from './type/polyline'
+export * from './type/radar'
+export * from './type/wall'
+
+/**
+ * circle material property
+ */
+
+export { default as CircleBlurMaterialProperty } from './property/circle/CircleBlurMaterialProperty'
+export { default as CircleDiffuseMaterialProperty } from './property/circle/CircleDiffuseMaterialProperty'
+export { default as CircleFadeMaterialProperty } from './property/circle/CircleFadeMaterialProperty'
+export { default as CirclePulseMaterialProperty } from './property/circle/CirclePulseMaterialProperty'
+export { default as CircleScanMaterialProperty } from './property/circle/CircleScanMaterialProperty'
+export { default as CircleSpiralMaterialProperty } from './property/circle/CircleSpiralMaterialProperty'
+export { default as CircleVaryMaterialProperty } from './property/circle/CircleVaryMaterialProperty'
+export { default as CircleWaveMaterialProperty } from './property/circle/CircleWaveMaterialProperty'
+
+/**
+ * ellipsoid material property
+ */
+export { default as EllipsoidElectricMaterialProperty } from './property/ellipsoid/EllipsoidElectricMaterialProperty'
+export { default as EllipsoidTrailMaterialProperty } from './property/ellipsoid/EllipsoidTrailMaterialProperty'
+
+/**
+ * polyline material property
+ */
+
+export { default as PolylineFlickerMaterialProperty } from './property/polyline/PolylineFlickerMaterialProperty'
+export { default as PolylineFlowMaterialProperty } from './property/polyline/PolylineFlowMaterialProperty'
+export { default as PolylineImageTrailMaterialProperty } from './property/polyline/PolylineImageTrailMaterialProperty'
+export { default as PolylineLightingMaterialProperty } from './property/polyline/PolylineLightingMaterialProperty'
+export { default as PolylineLightingTrailMaterialProperty } from './property/polyline/PolylineLightingTrailMaterialProperty'
+export { default as PolylineTrailMaterialProperty } from './property/polyline/PolylineTrailMaterialProperty'
+
+/**
+ * radar material property
+ */
+export { default as RadarLineMaterialProperty } from './property/radar/RadarLineMaterialProperty'
+export { default as RadarSweepMaterialProperty } from './property/radar/RadarSweepMaterialProperty'
+export { default as RadarWaveMaterialProperty } from './property/radar/RadarWaveMaterialProperty'
+
+/**
+ * wall material property
+ */
+export { default as WallImageTrailMaterialProperty } from './property/wall/WallImageTrailMaterialProperty'
+export { default as WallLineTrailMaterialProperty } from './property/wall/WallLineTrailMaterialProperty'
+export { default as WallTrailMaterialProperty } from './property/wall/WallTrailMaterialProperty'
+
+/**
+ * water material property
+ */
+export { default as WaterMaterialProperty } from './property/water/WaterMaterialProperty'
diff --git a/src/modules/material/property/circle/CircleBlurMaterialProperty.js b/src/modules/material/property/circle/CircleBlurMaterialProperty.js
new file mode 100644
index 00000000..941550f3
--- /dev/null
+++ b/src/modules/material/property/circle/CircleBlurMaterialProperty.js
@@ -0,0 +1,40 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-02-27 22:10:18
+ */
+
+import { Cesium } from '../../../../namespace'
+import MaterialProperty from '../../MaterialProperty'
+
+class CircleBlurMaterialProperty extends MaterialProperty {
+ constructor(options = {}) {
+ super(options)
+ }
+
+ getType(time) {
+ return Cesium.Material.CircleBlurType
+ }
+
+ getValue(time, result) {
+ result = Cesium.defaultValue(result, {})
+ result.color = Cesium.Property.getValueOrUndefined(this._color, time)
+ result.speed = this._speed
+ return result
+ }
+
+ equals(other) {
+ return (
+ this === other ||
+ (other instanceof CircleBlurMaterialProperty &&
+ Cesium.Property.equals(this._color, other._color) &&
+ Cesium.Property.equals(this._speed, other._speed))
+ )
+ }
+}
+
+Object.defineProperties(CircleBlurMaterialProperty.prototype, {
+ color: Cesium.createPropertyDescriptor('color'),
+ speed: Cesium.createPropertyDescriptor('speed'),
+})
+
+export default CircleBlurMaterialProperty
diff --git a/src/modules/material/property/circle/CircleDiffuseMaterialProperty.js b/src/modules/material/property/circle/CircleDiffuseMaterialProperty.js
new file mode 100644
index 00000000..d96185cf
--- /dev/null
+++ b/src/modules/material/property/circle/CircleDiffuseMaterialProperty.js
@@ -0,0 +1,40 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-02-27 22:10:18
+ */
+
+import { Cesium } from '../../../../namespace'
+import MaterialProperty from '../../MaterialProperty'
+
+class CircleDiffuseMaterialProperty extends MaterialProperty {
+ constructor(options = {}) {
+ super(options)
+ }
+
+ getType(time) {
+ return Cesium.Material.CircleDiffuseType
+ }
+
+ getValue(time, result) {
+ result = Cesium.defaultValue(result, {})
+ result.color = Cesium.Property.getValueOrUndefined(this._color, time)
+ result.speed = this._speed
+ return result
+ }
+
+ equals(other) {
+ return (
+ this === other ||
+ (other instanceof CircleDiffuseMaterialProperty &&
+ Cesium.Property.equals(this._color, other._color) &&
+ Cesium.Property.equals(this._speed, other._speed))
+ )
+ }
+}
+
+Object.defineProperties(CircleDiffuseMaterialProperty.prototype, {
+ color: Cesium.createPropertyDescriptor('color'),
+ speed: Cesium.createPropertyDescriptor('speed'),
+})
+
+export default CircleDiffuseMaterialProperty
diff --git a/src/modules/material/property/circle/CircleFadeMaterialProperty.js b/src/modules/material/property/circle/CircleFadeMaterialProperty.js
new file mode 100644
index 00000000..1149de11
--- /dev/null
+++ b/src/modules/material/property/circle/CircleFadeMaterialProperty.js
@@ -0,0 +1,42 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-03-06 17:56:39
+ */
+
+import { Cesium } from '../../../../namespace'
+import MaterialProperty from '../../MaterialProperty'
+
+class CircleFadeMaterialProperty extends MaterialProperty {
+ constructor(options = {}) {
+ super(options)
+ }
+
+ getType(time) {
+ return Cesium.Material.CircleFadeType
+ }
+
+ getValue(time, result) {
+ if (!result) {
+ result = {}
+ }
+ result.color = Cesium.Property.getValueOrUndefined(this._color, time)
+ result.speed = this._speed
+ return result
+ }
+
+ equals(other) {
+ return (
+ this === other ||
+ (other instanceof CircleFadeMaterialProperty &&
+ Cesium.Property.equals(this._color, other._color) &&
+ Cesium.Property.equals(this._speed, other._speed))
+ )
+ }
+}
+
+Object.defineProperties(CircleFadeMaterialProperty.prototype, {
+ color: Cesium.createPropertyDescriptor('color'),
+ speed: Cesium.createPropertyDescriptor('speed'),
+})
+
+export default CircleFadeMaterialProperty
diff --git a/src/modules/material/property/circle/CirclePulseMaterialProperty.js b/src/modules/material/property/circle/CirclePulseMaterialProperty.js
new file mode 100644
index 00000000..e66039c8
--- /dev/null
+++ b/src/modules/material/property/circle/CirclePulseMaterialProperty.js
@@ -0,0 +1,42 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-02-27 23:03:44
+ */
+
+import { Cesium } from '../../../../namespace'
+import MaterialProperty from '../../MaterialProperty'
+
+class CirclePulseMaterialProperty extends MaterialProperty {
+ constructor(options = {}) {
+ super(options)
+ }
+
+ getType(time) {
+ return Cesium.Material.CirclePulseType
+ }
+
+ getValue(time, result) {
+ if (!result) {
+ result = {}
+ }
+ result.color = Cesium.Property.getValueOrUndefined(this._color, time)
+ result.speed = this._speed
+ return result
+ }
+
+ equals(other) {
+ return (
+ this === other ||
+ (other instanceof CirclePulseMaterialProperty &&
+ Cesium.Property.equals(this._color, other._color) &&
+ Cesium.Property.equals(this._speed, other._speed))
+ )
+ }
+}
+
+Object.defineProperties(CirclePulseMaterialProperty.prototype, {
+ color: Cesium.createPropertyDescriptor('color'),
+ speed: Cesium.createPropertyDescriptor('speed'),
+})
+
+export default CirclePulseMaterialProperty
diff --git a/src/modules/material/property/circle/CircleScanMaterialProperty.js b/src/modules/material/property/circle/CircleScanMaterialProperty.js
new file mode 100644
index 00000000..511a7007
--- /dev/null
+++ b/src/modules/material/property/circle/CircleScanMaterialProperty.js
@@ -0,0 +1,42 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-01-09 20:23:53
+ */
+
+import { Cesium } from '../../../../namespace'
+import MaterialProperty from '../../MaterialProperty'
+
+class CircleScanMaterialProperty extends MaterialProperty {
+ constructor(options = {}) {
+ super(options)
+ }
+
+ getType(time) {
+ return Cesium.Material.CircleScanType
+ }
+
+ getValue(time, result) {
+ if (!result) {
+ result = {}
+ }
+ result.color = Cesium.Property.getValueOrUndefined(this._color, time)
+ result.speed = this._speed
+ return result
+ }
+
+ equals(other) {
+ return (
+ this === other ||
+ (other instanceof CircleScanMaterialProperty &&
+ Cesium.Property.equals(this._color, other._color) &&
+ Cesium.Property.equals(this._speed, other._speed))
+ )
+ }
+}
+
+Object.defineProperties(CircleScanMaterialProperty.prototype, {
+ color: Cesium.createPropertyDescriptor('color'),
+ speed: Cesium.createPropertyDescriptor('speed'),
+})
+
+export default CircleScanMaterialProperty
diff --git a/src/modules/material/property/circle/CircleSpiralMaterialProperty.js b/src/modules/material/property/circle/CircleSpiralMaterialProperty.js
new file mode 100644
index 00000000..f3c16797
--- /dev/null
+++ b/src/modules/material/property/circle/CircleSpiralMaterialProperty.js
@@ -0,0 +1,42 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-01-09 20:23:53
+ */
+
+import { Cesium } from '../../../../namespace'
+import MaterialProperty from '../../MaterialProperty'
+
+class CircleSpiralMaterialProperty extends MaterialProperty {
+ constructor(options = {}) {
+ super(options)
+ }
+
+ getType(time) {
+ return Cesium.Material.CircleSpiralType
+ }
+
+ getValue(time, result) {
+ if (!result) {
+ result = {}
+ }
+ result.color = Cesium.Property.getValueOrUndefined(this._color, time)
+ result.speed = this._speed
+ return result
+ }
+
+ equals(other) {
+ return (
+ this === other ||
+ (other instanceof CircleSpiralMaterialProperty &&
+ Cesium.Property.equals(this._color, other._color) &&
+ Cesium.Property.equals(this._speed, other._speed))
+ )
+ }
+}
+
+Object.defineProperties(CircleSpiralMaterialProperty.prototype, {
+ color: Cesium.createPropertyDescriptor('color'),
+ speed: Cesium.createPropertyDescriptor('speed'),
+})
+
+export default CircleSpiralMaterialProperty
diff --git a/src/modules/material/property/circle/CircleVaryMaterialProperty.js b/src/modules/material/property/circle/CircleVaryMaterialProperty.js
new file mode 100644
index 00000000..143bccb9
--- /dev/null
+++ b/src/modules/material/property/circle/CircleVaryMaterialProperty.js
@@ -0,0 +1,40 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-02-27 22:10:18
+ */
+
+import { Cesium } from '../../../../namespace'
+import MaterialProperty from '../../MaterialProperty'
+
+class CircleVaryMaterialProperty extends MaterialProperty {
+ constructor(options = {}) {
+ super(options)
+ }
+
+ getType(time) {
+ return Cesium.Material.CircleVaryType
+ }
+
+ getValue(time, result) {
+ result = Cesium.defaultValue(result, {})
+ result.color = Cesium.Property.getValueOrUndefined(this._color, time)
+ result.speed = this._speed
+ return result
+ }
+
+ equals(other) {
+ return (
+ this === other ||
+ (other instanceof CircleVaryMaterialProperty &&
+ Cesium.Property.equals(this._color, other._color) &&
+ Cesium.Property.equals(this._speed, other._speed))
+ )
+ }
+}
+
+Object.defineProperties(CircleVaryMaterialProperty.prototype, {
+ color: Cesium.createPropertyDescriptor('color'),
+ speed: Cesium.createPropertyDescriptor('speed'),
+})
+
+export default CircleVaryMaterialProperty
diff --git a/src/modules/material/property/circle/CircleWaveMaterialProperty.js b/src/modules/material/property/circle/CircleWaveMaterialProperty.js
new file mode 100644
index 00000000..9bd8902c
--- /dev/null
+++ b/src/modules/material/property/circle/CircleWaveMaterialProperty.js
@@ -0,0 +1,54 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-03-06 17:56:39
+ */
+
+import { Cesium } from '../../../../namespace'
+import MaterialProperty from '../../MaterialProperty'
+
+class CircleWaveMaterialProperty extends MaterialProperty {
+ constructor(options = {}) {
+ super(options)
+ this.count = Math.max(options.count || 3, 1)
+ this.gradient = Cesium.Math.clamp(options.gradient || 0.1, 0, 1)
+ }
+
+ get isConstant() {
+ return false
+ }
+
+ get definitionChanged() {
+ return this._definitionChanged
+ }
+
+ getType(time) {
+ return Cesium.Material.CircleWaveType
+ }
+
+ getValue(time, result) {
+ if (!result) {
+ result = {}
+ }
+ result.color = Cesium.Property.getValueOrUndefined(this._color, time)
+ result.speed = this._speed
+ result.count = this.count
+ result.gradient = this.gradient
+ return result
+ }
+
+ equals(other) {
+ return (
+ this === other ||
+ (other instanceof CircleWaveMaterialProperty &&
+ Cesium.Property.equals(this._color, other._color) &&
+ Cesium.Property.equals(this._speed, other._speed))
+ )
+ }
+}
+
+Object.defineProperties(CircleWaveMaterialProperty.prototype, {
+ color: Cesium.createPropertyDescriptor('color'),
+ speed: Cesium.createPropertyDescriptor('speed'),
+})
+
+export default CircleWaveMaterialProperty
diff --git a/src/modules/material/property/ellipsoid/EllipsoidElectricMaterialProperty.js b/src/modules/material/property/ellipsoid/EllipsoidElectricMaterialProperty.js
new file mode 100644
index 00000000..5246b1a2
--- /dev/null
+++ b/src/modules/material/property/ellipsoid/EllipsoidElectricMaterialProperty.js
@@ -0,0 +1,40 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-03-04 22:10:18
+ */
+
+import { Cesium } from '../../../../namespace'
+import MaterialProperty from '../../MaterialProperty'
+
+class EllipsoidElectricMaterialProperty extends MaterialProperty {
+ constructor(options = {}) {
+ super(options)
+ }
+
+ getType(time) {
+ return Cesium.Material.EllipsoidElectricType
+ }
+
+ getValue(time, result) {
+ result = Cesium.defaultValue(result, {})
+ result.color = Cesium.Property.getValueOrUndefined(this._color, time)
+ result.speed = this._speed
+ return result
+ }
+
+ equals(other) {
+ return (
+ this === other ||
+ (other instanceof EllipsoidElectricMaterialProperty &&
+ Cesium.Property.equals(this._color, other._color) &&
+ Cesium.Property.equals(this._speed, other._speed))
+ )
+ }
+}
+
+Object.defineProperties(EllipsoidElectricMaterialProperty.prototype, {
+ color: Cesium.createPropertyDescriptor('color'),
+ speed: Cesium.createPropertyDescriptor('speed'),
+})
+
+export default EllipsoidElectricMaterialProperty
diff --git a/src/modules/material/property/ellipsoid/EllipsoidTrailMaterialProperty.js b/src/modules/material/property/ellipsoid/EllipsoidTrailMaterialProperty.js
new file mode 100644
index 00000000..3ac422ac
--- /dev/null
+++ b/src/modules/material/property/ellipsoid/EllipsoidTrailMaterialProperty.js
@@ -0,0 +1,40 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-03-04 22:10:18
+ */
+
+import { Cesium } from '../../../../namespace'
+import MaterialProperty from '../../MaterialProperty'
+
+class EllipsoidTrailMaterialProperty extends MaterialProperty {
+ constructor(options = {}) {
+ super(options)
+ }
+
+ getType(time) {
+ return Cesium.Material.EllipsoidTrailType
+ }
+
+ getValue(time, result) {
+ result = Cesium.defaultValue(result, {})
+ result.color = Cesium.Property.getValueOrUndefined(this._color, time)
+ result.speed = this._speed
+ return result
+ }
+
+ equals(other) {
+ return (
+ this === other ||
+ (other instanceof EllipsoidTrailMaterialProperty &&
+ Cesium.Property.equals(this._color, other._color) &&
+ Cesium.Property.equals(this._speed, other._speed))
+ )
+ }
+}
+
+Object.defineProperties(EllipsoidTrailMaterialProperty.prototype, {
+ color: Cesium.createPropertyDescriptor('color'),
+ speed: Cesium.createPropertyDescriptor('speed'),
+})
+
+export default EllipsoidTrailMaterialProperty
diff --git a/src/modules/material/property/polyline/PolylineEmissionMaterialProperty.js b/src/modules/material/property/polyline/PolylineEmissionMaterialProperty.js
new file mode 100644
index 00000000..42f445ea
--- /dev/null
+++ b/src/modules/material/property/polyline/PolylineEmissionMaterialProperty.js
@@ -0,0 +1,39 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-02-26 10:15:55
+ */
+
+import { Cesium } from '../../../../namespace'
+import MaterialProperty from '../../MaterialProperty'
+
+class PolylineEmissionMaterialProperty extends MaterialProperty {
+ constructor(options = {}) {
+ super(options)
+ }
+
+ getType(time) {
+ return Cesium.Material.PolylineEmissionType
+ }
+
+ getValue(time, result) {
+ if (!result) {
+ result = {}
+ }
+ result.color = Cesium.Property.getValueOrUndefined(this._color, time)
+ return result
+ }
+
+ equals(other) {
+ return (
+ this === other ||
+ (other instanceof PolylineEmissionMaterialProperty &&
+ Cesium.Property.equals(this._color, other._color))
+ )
+ }
+}
+
+Object.defineProperties(PolylineEmissionMaterialProperty.prototype, {
+ color: Cesium.createPropertyDescriptor('color'),
+})
+
+export default PolylineEmissionMaterialProperty
diff --git a/src/modules/material/property/polyline/PolylineFlickerMaterialProperty.js b/src/modules/material/property/polyline/PolylineFlickerMaterialProperty.js
new file mode 100644
index 00000000..f25da9bb
--- /dev/null
+++ b/src/modules/material/property/polyline/PolylineFlickerMaterialProperty.js
@@ -0,0 +1,42 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-01-11 21:08:02
+ */
+
+import { Cesium } from '../../../../namespace'
+import MaterialProperty from '../../MaterialProperty'
+
+class PolylineFlickerMaterialProperty extends MaterialProperty {
+ constructor(options = {}) {
+ super(options)
+ }
+
+ getType(time) {
+ return Cesium.Material.PolylineFlickerType
+ }
+
+ getValue(time, result) {
+ if (!result) {
+ result = {}
+ }
+ result.color = Cesium.Property.getValueOrUndefined(this._color, time)
+ result.speed = this._speed
+ return result
+ }
+
+ equals(other) {
+ return (
+ this === other ||
+ (other instanceof PolylineFlickerMaterialProperty &&
+ Cesium.Property.equals(this._color, other._color) &&
+ Cesium.Property.equals(this._speed, other._speed))
+ )
+ }
+}
+
+Object.defineProperties(PolylineFlickerMaterialProperty.prototype, {
+ color: Cesium.createPropertyDescriptor('color'),
+ speed: Cesium.createPropertyDescriptor('speed'),
+})
+
+export default PolylineFlickerMaterialProperty
diff --git a/src/modules/material/property/polyline/PolylineFlowMaterialProperty.js b/src/modules/material/property/polyline/PolylineFlowMaterialProperty.js
new file mode 100644
index 00000000..603d5732
--- /dev/null
+++ b/src/modules/material/property/polyline/PolylineFlowMaterialProperty.js
@@ -0,0 +1,54 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-02-24 13:53:52
+ */
+
+import { Cesium } from '../../../../namespace'
+import MaterialProperty from '../../MaterialProperty'
+
+class PolylineFlowMaterialProperty extends MaterialProperty {
+ constructor(options = {}) {
+ super(options)
+ this._percent = undefined
+ this._percentSubscription = undefined
+ this._gradient = undefined
+ this._gradientSubscription = undefined
+ this.percent = options.percent || 0.03
+ this.gradient = options.gradient || 0.1
+ }
+
+ getType(time) {
+ return Cesium.Material.PolylineFlowType
+ }
+
+ getValue(time, result) {
+ if (!result) {
+ result = {}
+ }
+ result.color = Cesium.Property.getValueOrUndefined(this._color, time)
+ result.speed = this._speed
+ result.percent = this._percent
+ result.gradient = this._gradient
+ return result
+ }
+
+ equals(other) {
+ return (
+ this === other ||
+ (other instanceof PolylineFlowMaterialProperty &&
+ Cesium.Property.equals(this._color, other._color) &&
+ Cesium.Property.equals(this._speed, other._speed) &&
+ Cesium.Property.equals(this._percent, other._percent) &&
+ Cesium.Property.equals(this._gradient, other._gradient))
+ )
+ }
+}
+
+Object.defineProperties(PolylineFlowMaterialProperty.prototype, {
+ color: Cesium.createPropertyDescriptor('color'),
+ speed: Cesium.createPropertyDescriptor('speed'),
+ percent: Cesium.createPropertyDescriptor('percent'),
+ gradient: Cesium.createPropertyDescriptor('gradient'),
+})
+
+export default PolylineFlowMaterialProperty
diff --git a/src/modules/material/property/polyline/PolylineImageTrailMaterialProperty.js b/src/modules/material/property/polyline/PolylineImageTrailMaterialProperty.js
new file mode 100644
index 00000000..a0af795a
--- /dev/null
+++ b/src/modules/material/property/polyline/PolylineImageTrailMaterialProperty.js
@@ -0,0 +1,57 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-07-17 22:15:56
+ */
+
+import { Cesium } from '../../../../namespace'
+import MaterialProperty from '../../MaterialProperty'
+
+class PolylineImageTrailMaterialProperty extends MaterialProperty {
+ constructor(options = {}) {
+ super(options)
+ this._image = undefined
+ this._imageSubscription = undefined
+ this._repeat = undefined
+ this._repeatSubscription = undefined
+ this.image = options.image
+ this.repeat = new Cesium.Cartesian2(
+ options.repeat?.x || 1,
+ options.repeat?.y || 1
+ )
+ }
+
+ getType(time) {
+ return Cesium.Material.PolylineImageTrailType
+ }
+
+ getValue(time, result) {
+ if (!result) {
+ result = {}
+ }
+ result.color = Cesium.Property.getValueOrUndefined(this._color, time)
+ result.image = Cesium.Property.getValueOrUndefined(this._image, time)
+ result.repeat = Cesium.Property.getValueOrUndefined(this._repeat, time)
+ result.speed = this._speed
+ return result
+ }
+
+ equals(other) {
+ return (
+ this === other ||
+ (other instanceof PolylineImageTrailMaterialProperty &&
+ Cesium.Property.equals(this._color, other._color) &&
+ Cesium.Property.equals(this._image, other._image) &&
+ Cesium.Property.equals(this._repeat, other._repeat) &&
+ Cesium.Property.equals(this._speed, other._speed))
+ )
+ }
+}
+
+Object.defineProperties(PolylineImageTrailMaterialProperty.prototype, {
+ color: Cesium.createPropertyDescriptor('color'),
+ speed: Cesium.createPropertyDescriptor('speed'),
+ image: Cesium.createPropertyDescriptor('image'),
+ repeat: Cesium.createPropertyDescriptor('repeat'),
+})
+
+export default PolylineImageTrailMaterialProperty
diff --git a/src/modules/material/property/polyline/PolylineLightingMaterialProperty.js b/src/modules/material/property/polyline/PolylineLightingMaterialProperty.js
new file mode 100644
index 00000000..fff1e469
--- /dev/null
+++ b/src/modules/material/property/polyline/PolylineLightingMaterialProperty.js
@@ -0,0 +1,46 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-01-13 20:52:47
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+import MaterialProperty from '../../MaterialProperty'
+const IMG = require('@dc-modules/images/lighting.png')
+
+class PolylineLightingMaterialProperty extends MaterialProperty {
+ constructor(options = {}) {
+ super(options)
+ this._image = undefined
+ this._imageSubscription = undefined
+ this.image = IMG
+ }
+
+ getType(time) {
+ return Cesium.Material.PolylineLightingType
+ }
+
+ getValue(time, result) {
+ if (!result) {
+ result = {}
+ }
+ result.color = Cesium.Property.getValueOrUndefined(this._color, time)
+ result.image = Cesium.Property.getValueOrUndefined(this._image, time)
+ return result
+ }
+
+ equals(other) {
+ return (
+ this === other ||
+ (other instanceof PolylineLightingMaterialProperty &&
+ Cesium.Property.equals(this._color, other._color) &&
+ Cesium.Property.equals(this._image, other._image))
+ )
+ }
+}
+
+Object.defineProperties(PolylineLightingMaterialProperty.prototype, {
+ color: Cesium.createPropertyDescriptor('color'),
+ image: Cesium.createPropertyDescriptor('image')
+})
+
+export default PolylineLightingMaterialProperty
diff --git a/src/modules/material/property/polyline/PolylineLightingTrailMaterialProperty.js b/src/modules/material/property/polyline/PolylineLightingTrailMaterialProperty.js
new file mode 100644
index 00000000..cfca4399
--- /dev/null
+++ b/src/modules/material/property/polyline/PolylineLightingTrailMaterialProperty.js
@@ -0,0 +1,49 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-01-13 20:52:47
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+import MaterialProperty from '../../MaterialProperty'
+
+const IMG = require('@dc-modules/images/lighting.png')
+
+class PolylineLightingTrailMaterialProperty extends MaterialProperty {
+ constructor(options = {}) {
+ super(options)
+ this._image = undefined
+ this._imageSubscription = undefined
+ this.image = IMG
+ }
+
+ getType(time) {
+ return Cesium.Material.PolylineLightingTrailType
+ }
+
+ getValue(time, result) {
+ if (!result) {
+ result = {}
+ }
+ result.color = Cesium.Property.getValueOrUndefined(this._color, time)
+ result.image = Cesium.Property.getValueOrUndefined(this._image, time)
+ result.speed = this._speed
+ return result
+ }
+
+ equals(other) {
+ return (
+ this === other ||
+ (other instanceof PolylineLightingTrailMaterialProperty &&
+ Cesium.Property.equals(this._color, other._color) &&
+ Cesium.Property.equals(this._speed, other._speed))
+ )
+ }
+}
+
+Object.defineProperties(PolylineLightingTrailMaterialProperty.prototype, {
+ color: Cesium.createPropertyDescriptor('color'),
+ speed: Cesium.createPropertyDescriptor('speed'),
+ image: Cesium.createPropertyDescriptor('image')
+})
+
+export default PolylineLightingTrailMaterialProperty
diff --git a/src/modules/material/property/polyline/PolylineTrailMaterialProperty.js b/src/modules/material/property/polyline/PolylineTrailMaterialProperty.js
new file mode 100644
index 00000000..39768e6c
--- /dev/null
+++ b/src/modules/material/property/polyline/PolylineTrailMaterialProperty.js
@@ -0,0 +1,42 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-02-24 13:09:09
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+import MaterialProperty from '../../MaterialProperty'
+
+class PolylineTrailMaterialProperty extends MaterialProperty {
+ constructor(options = {}) {
+ super(options)
+ }
+
+ getType(time) {
+ return Cesium.Material.PolylineTrailType
+ }
+
+ getValue(time, result) {
+ if (!result) {
+ result = {}
+ }
+ result.color = Cesium.Property.getValueOrUndefined(this._color, time)
+ result.speed = this._speed
+ return result
+ }
+
+ equals(other) {
+ return (
+ this === other ||
+ (other instanceof PolylineTrailMaterialProperty &&
+ Cesium.Property.equals(this._color, other._color) &&
+ Cesium.Property.equals(this._speed, other._speed))
+ )
+ }
+}
+
+Object.defineProperties(PolylineTrailMaterialProperty.prototype, {
+ color: Cesium.createPropertyDescriptor('color'),
+ speed: Cesium.createPropertyDescriptor('speed')
+})
+
+export default PolylineTrailMaterialProperty
diff --git a/src/modules/material/property/radar/RadarLineMaterialProperty.js b/src/modules/material/property/radar/RadarLineMaterialProperty.js
new file mode 100644
index 00000000..48dc607f
--- /dev/null
+++ b/src/modules/material/property/radar/RadarLineMaterialProperty.js
@@ -0,0 +1,40 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-02-27 23:53:08
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+import MaterialProperty from '../../MaterialProperty'
+
+class RadarLineMaterialProperty extends MaterialProperty {
+ constructor(options = {}) {
+ super(options)
+ }
+
+ getType(time) {
+ return Cesium.Material.RadarLineType
+ }
+
+ getValue(time, result) {
+ result = Cesium.defaultValue(result, {})
+ result.color = Cesium.Property.getValueOrUndefined(this._color, time)
+ result.speed = this._speed
+ return result
+ }
+
+ equals(other) {
+ return (
+ this === other ||
+ (other instanceof RadarLineMaterialProperty &&
+ Cesium.Property.equals(this._color, other._color) &&
+ Cesium.Property.equals(this._speed, other._speed))
+ )
+ }
+}
+
+Object.defineProperties(RadarLineMaterialProperty.prototype, {
+ color: Cesium.createPropertyDescriptor('color'),
+ speed: Cesium.createPropertyDescriptor('speed')
+})
+
+export default RadarLineMaterialProperty
diff --git a/src/modules/material/property/radar/RadarSweepMaterialProperty.js b/src/modules/material/property/radar/RadarSweepMaterialProperty.js
new file mode 100644
index 00000000..c2968bfd
--- /dev/null
+++ b/src/modules/material/property/radar/RadarSweepMaterialProperty.js
@@ -0,0 +1,40 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-02-27 23:53:08
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+import MaterialProperty from '../../MaterialProperty'
+
+class RadarSweepMaterialProperty extends MaterialProperty {
+ constructor(options = {}) {
+ super(options)
+ }
+
+ getType(time) {
+ return Cesium.Material.RadarSweepType
+ }
+
+ getValue(time, result) {
+ result = Cesium.defaultValue(result, {})
+ result.color = Cesium.Property.getValueOrUndefined(this._color, time)
+ result.speed = this._speed
+ return result
+ }
+
+ equals(other) {
+ return (
+ this === other ||
+ (other instanceof RadarSweepMaterialProperty &&
+ Cesium.Property.equals(this._color, other._color) &&
+ Cesium.Property.equals(this._speed, other._speed))
+ )
+ }
+}
+
+Object.defineProperties(RadarSweepMaterialProperty.prototype, {
+ color: Cesium.createPropertyDescriptor('color'),
+ speed: Cesium.createPropertyDescriptor('speed')
+})
+
+export default RadarSweepMaterialProperty
diff --git a/src/modules/material/property/radar/RadarWaveMaterialProperty.js b/src/modules/material/property/radar/RadarWaveMaterialProperty.js
new file mode 100644
index 00000000..857bc1cf
--- /dev/null
+++ b/src/modules/material/property/radar/RadarWaveMaterialProperty.js
@@ -0,0 +1,40 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-02-27 23:53:08
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+import MaterialProperty from '../../MaterialProperty'
+
+class RadarWaveMaterialProperty extends MaterialProperty {
+ constructor(options = {}) {
+ super(options)
+ }
+
+ getType(time) {
+ return Cesium.Material.RadarWaveType
+ }
+
+ getValue(time, result) {
+ result = Cesium.defaultValue(result, {})
+ result.color = Cesium.Property.getValueOrUndefined(this._color, time)
+ result.speed = this._speed
+ return result
+ }
+
+ equals(other) {
+ return (
+ this === other ||
+ (other instanceof RadarWaveMaterialProperty &&
+ Cesium.Property.equals(this._color, other._color) &&
+ Cesium.Property.equals(this._speed, other._speed))
+ )
+ }
+}
+
+Object.defineProperties(RadarWaveMaterialProperty.prototype, {
+ color: Cesium.createPropertyDescriptor('color'),
+ speed: Cesium.createPropertyDescriptor('speed')
+})
+
+export default RadarWaveMaterialProperty
diff --git a/src/modules/material/property/wall/WallImageTrailMaterialProperty.js b/src/modules/material/property/wall/WallImageTrailMaterialProperty.js
new file mode 100644
index 00000000..98634344
--- /dev/null
+++ b/src/modules/material/property/wall/WallImageTrailMaterialProperty.js
@@ -0,0 +1,55 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-02-27 23:53:08
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+import MaterialProperty from '../../MaterialProperty'
+
+class WallImageTrailMaterialProperty extends MaterialProperty {
+ constructor(options = {}) {
+ super(options)
+ this._image = undefined
+ this._imageSubscription = undefined
+ this._repeat = undefined
+ this._repeatSubscription = undefined
+ this.image = options.image
+ this.repeat = new Cesium.Cartesian2(
+ options.repeat?.x || 1,
+ options.repeat?.y || 1
+ )
+ }
+
+ getType(time) {
+ return Cesium.Material.WallImageTrailType
+ }
+
+ getValue(time, result) {
+ result = Cesium.defaultValue(result, {})
+ result.color = Cesium.Property.getValueOrUndefined(this._color, time)
+ result.image = Cesium.Property.getValueOrUndefined(this._image, time)
+ result.repeat = Cesium.Property.getValueOrUndefined(this._repeat, time)
+ result.speed = this._speed
+ return result
+ }
+
+ equals(other) {
+ return (
+ this === other ||
+ (other instanceof WallImageTrailMaterialProperty &&
+ Cesium.Property.equals(this._color, other._color) &&
+ Cesium.Property.equals(this._image, other._image) &&
+ Cesium.Property.equals(this._repeat, other._repeat) &&
+ Cesium.Property.equals(this._speed, other._speed))
+ )
+ }
+}
+
+Object.defineProperties(WallImageTrailMaterialProperty.prototype, {
+ image: Cesium.createPropertyDescriptor('image'),
+ color: Cesium.createPropertyDescriptor('color'),
+ speed: Cesium.createPropertyDescriptor('speed'),
+ repeat: Cesium.createPropertyDescriptor('repeat')
+})
+
+export default WallImageTrailMaterialProperty
diff --git a/src/modules/material/property/wall/WallLineTrailMaterialProperty.js b/src/modules/material/property/wall/WallLineTrailMaterialProperty.js
new file mode 100644
index 00000000..7613e07a
--- /dev/null
+++ b/src/modules/material/property/wall/WallLineTrailMaterialProperty.js
@@ -0,0 +1,57 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-02-24 13:54:09
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+import MaterialProperty from '../../MaterialProperty'
+const IMG = require('@dc-modules/images/space_line.png')
+
+class WallLineTrailMaterialProperty extends MaterialProperty {
+ constructor(options = {}) {
+ super(options)
+ this._image = undefined
+ this._imageSubscription = undefined
+ this._repeat = undefined
+ this._repeatSubscription = undefined
+ this.image = IMG
+ this.repeat = new Cesium.Cartesian2(
+ options.repeat?.x || 1,
+ options.repeat?.y || 1
+ )
+ }
+
+ getType(time) {
+ return Cesium.Material.WallLineTrailType
+ }
+
+ getValue(time, result) {
+ if (!result) {
+ result = {}
+ }
+ result.color = Cesium.Property.getValueOrUndefined(this._color, time)
+ result.image = Cesium.Property.getValueOrUndefined(this._image, time)
+ result.repeat = Cesium.Property.getValueOrUndefined(this._repeat, time)
+ result.speed = this._speed
+ return result
+ }
+
+ equals(other) {
+ return (
+ this === other ||
+ (other instanceof WallLineTrailMaterialProperty &&
+ Cesium.Property.equals(this._color, other._color) &&
+ Cesium.Property.equals(this._speed, other._speed) &&
+ Cesium.Property.equals(this._repeat, other._repeat))
+ )
+ }
+}
+
+Object.defineProperties(WallLineTrailMaterialProperty.prototype, {
+ color: Cesium.createPropertyDescriptor('color'),
+ image: Cesium.createPropertyDescriptor('image'),
+ repeat: Cesium.createPropertyDescriptor('repeat'),
+ speed: Cesium.createPropertyDescriptor('speed')
+})
+
+export default WallLineTrailMaterialProperty
diff --git a/src/modules/material/property/wall/WallTrailMaterialProperty.js b/src/modules/material/property/wall/WallTrailMaterialProperty.js
new file mode 100644
index 00000000..61e3b106
--- /dev/null
+++ b/src/modules/material/property/wall/WallTrailMaterialProperty.js
@@ -0,0 +1,49 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-06-22 16:46:14
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+import MaterialProperty from '../../MaterialProperty'
+
+const IMG = require('@dc-modules/images/fence.png')
+
+class WallTrailMaterialProperty extends MaterialProperty {
+ constructor(options = {}) {
+ super(options)
+ this._image = undefined
+ this._imageSubscription = undefined
+ this.image = IMG
+ }
+
+ getType(time) {
+ return Cesium.Material.WallTrailType
+ }
+
+ getValue(time, result) {
+ if (!result) {
+ result = {}
+ }
+ result.color = Cesium.Property.getValueOrUndefined(this._color, time)
+ result.image = Cesium.Property.getValueOrUndefined(this._image, time)
+ result.speed = this._speed
+ return result
+ }
+
+ equals(other) {
+ return (
+ this === other ||
+ (other instanceof WallTrailMaterialProperty &&
+ Cesium.Property.equals(this._color, other._color) &&
+ Cesium.Property.equals(this._speed, other._speed))
+ )
+ }
+}
+
+Object.defineProperties(WallTrailMaterialProperty.prototype, {
+ color: Cesium.createPropertyDescriptor('color'),
+ speed: Cesium.createPropertyDescriptor('speed'),
+ image: Cesium.createPropertyDescriptor('image')
+})
+
+export default WallTrailMaterialProperty
diff --git a/src/modules/material/property/water/WaterMaterialProperty.js b/src/modules/material/property/water/WaterMaterialProperty.js
new file mode 100644
index 00000000..7826d8ab
--- /dev/null
+++ b/src/modules/material/property/water/WaterMaterialProperty.js
@@ -0,0 +1,87 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-02-25 21:16:00
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+
+class WaterMaterialProperty {
+ constructor(options) {
+ options = options || {}
+ this._definitionChanged = new Cesium.Event()
+ this._baseWaterColor = undefined
+ this._baseWaterColorSubscription = undefined
+ this.baseWaterColor =
+ options.baseWaterColor || new Cesium.Color(0.2, 0.3, 0.6, 1.0)
+ this._blendColor = undefined
+ this._blendColorSubscription = undefined
+ this.blendColor =
+ options.blendColor || new Cesium.Color(0.0, 1.0, 0.699, 1.0)
+ this._specularMap = undefined
+ this._specularMapSubscription = undefined
+ this.specularMap = options.specularMap || Cesium.Material.DefaultImageId
+ this._normalMap = undefined
+ this._normalMapSubscription = undefined
+ this.normalMap = options.normalMap || Cesium.Material.DefaultImageId
+ this.frequency = Cesium.defaultValue(options.frequency, 1000)
+ this.animationSpeed = Cesium.defaultValue(options.animationSpeed, 0.01)
+ this.amplitude = Cesium.defaultValue(options.amplitude, 10.0)
+ this.specularIntensity = Cesium.defaultValue(options.specularIntensity, 0.5)
+ }
+
+ get isConstant() {
+ return false
+ }
+
+ get definitionChanged() {
+ return this._definitionChanged
+ }
+
+ getType(time) {
+ return Cesium.Material.WaterType
+ }
+
+ getValue(time, result) {
+ if (!result) {
+ result = {}
+ }
+ result.baseWaterColor = Cesium.Property.getValueOrUndefined(
+ this._baseWaterColor,
+ time
+ )
+ result.blendColor = Cesium.Property.getValueOrUndefined(
+ this._blendColor,
+ time
+ )
+ result.specularMap = Cesium.Property.getValueOrUndefined(
+ this._specularMap,
+ time
+ )
+ result.normalMap = Cesium.Property.getValueOrUndefined(
+ this._normalMap,
+ time
+ )
+ result.frequency = this.frequency
+ result.animationSpeed = this.animationSpeed
+ result.amplitude = this.amplitude
+ result.specularIntensity = this.specularIntensity
+ return result
+ }
+
+ equals(other) {
+ return (
+ this === other ||
+ (other instanceof WaterMaterialProperty &&
+ Cesium.Property.equals(this._baseWaterColor, other._baseWaterColor))
+ )
+ }
+}
+
+Object.defineProperties(WaterMaterialProperty.prototype, {
+ baseWaterColor: Cesium.createPropertyDescriptor('baseWaterColor'),
+ blendColor: Cesium.createPropertyDescriptor('blendColor'),
+ specularMap: Cesium.createPropertyDescriptor('specularMap'),
+ normalMap: Cesium.createPropertyDescriptor('normalMap')
+})
+
+export default WaterMaterialProperty
diff --git a/src/modules/material/shader/circle/CircleBlurMaterial.glsl b/src/modules/material/shader/circle/CircleBlurMaterial.glsl
new file mode 100644
index 00000000..c01c277d
--- /dev/null
+++ b/src/modules/material/shader/circle/CircleBlurMaterial.glsl
@@ -0,0 +1,17 @@
+uniform vec4 color;
+uniform float speed;
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ vec2 st = materialInput.st ;
+ vec2 center = vec2(0.5);
+ float time = fract(czm_frameNumber * speed / 1000.0);
+ float r = 0.5 + sin(time) / 3.0;
+ float dis = distance(st, center);
+ float a = 0.0;
+ if(dis < r) {
+ a = 1.0 - smoothstep(0.0, r, dis);
+ }
+ material.alpha = pow(a,10.0) ;
+ material.diffuse = color.rgb * a * 3.0;
+ return material;
+}
diff --git a/src/modules/material/shader/circle/CircleDiffuseMaterial.glsl b/src/modules/material/shader/circle/CircleDiffuseMaterial.glsl
new file mode 100644
index 00000000..1b2f7844
--- /dev/null
+++ b/src/modules/material/shader/circle/CircleDiffuseMaterial.glsl
@@ -0,0 +1,24 @@
+uniform vec4 color;
+uniform float speed;
+
+vec3 circlePing(float r, float innerTail, float frontierBorder, float timeResetSeconds, float radarPingSpeed, float fadeDistance){
+ float t = fract(czm_frameNumber * speed / 1000.0);
+ float time = mod(t, timeResetSeconds) * radarPingSpeed;
+ float circle;
+ circle += smoothstep(time - innerTail, time, r) * smoothstep(time + frontierBorder,time, r);
+ circle *= smoothstep(fadeDistance, 0.0, r);
+ return vec3(circle);
+}
+
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ vec2 st = materialInput.st * 2.0 - 1.0 ;
+ vec2 center = vec2(0.);
+ float time = fract(czm_frameNumber * speed / 1000.0);
+ vec3 flagColor;
+ float r = length(st - center) / 4.;
+ flagColor += circlePing(r, 0.25, 0.025, 4.0, 0.3, 1.0) * color.rgb;
+ material.alpha = length(flagColor);
+ material.diffuse = flagColor.rgb;
+ return material;
+}
diff --git a/src/modules/material/shader/circle/CircleFadeMaterial.glsl b/src/modules/material/shader/circle/CircleFadeMaterial.glsl
new file mode 100644
index 00000000..607ca832
--- /dev/null
+++ b/src/modules/material/shader/circle/CircleFadeMaterial.glsl
@@ -0,0 +1,16 @@
+uniform vec4 color;
+uniform float speed;
+
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ material.diffuse = 1.5 * color.rgb;
+ vec2 st = materialInput.st;
+ float dis = distance(st, vec2(0.5, 0.5));
+ float per = fract(czm_frameNumber * speed / 1000.0);
+ if(dis > per * 0.5){
+ material.alpha = color.a;
+ }else {
+ discard;
+ }
+ return material;
+}
diff --git a/src/modules/material/shader/circle/CirclePulseMaterial.glsl b/src/modules/material/shader/circle/CirclePulseMaterial.glsl
new file mode 100644
index 00000000..39324e3d
--- /dev/null
+++ b/src/modules/material/shader/circle/CirclePulseMaterial.glsl
@@ -0,0 +1,16 @@
+uniform vec4 color;
+uniform float speed;
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ vec2 st = materialInput.st * 2.0 - 1.0;
+ float time = fract(czm_frameNumber * speed / 1000.0);
+ float r = length(st) * 1.2;
+ float a = pow(r, 2.0);
+ float b = sin(r * 0.8 - 1.6);
+ float c = sin(r - 0.010);
+ float s = sin(a - time * 2.0 + b) * c;
+ float d = abs(1.0 / (s * 10.8)) - 0.01;
+ material.alpha = pow(d,10.0) ;
+ material.diffuse = color.rgb * d;
+ return material;
+}
diff --git a/src/modules/material/shader/circle/CircleRingMaterial.glsl b/src/modules/material/shader/circle/CircleRingMaterial.glsl
new file mode 100644
index 00000000..f7d50c8f
--- /dev/null
+++ b/src/modules/material/shader/circle/CircleRingMaterial.glsl
@@ -0,0 +1,17 @@
+uniform vec4 color;
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ vec2 st = materialInput.st;
+ vec2 center = st - vec2(0.5,0.5);
+ float length = length(center) / 0.5;
+ float time = 1. - abs(czm_frameNumber / 360. - 0.5);
+ float param = 1. - step(length, 0.6); //大于0.6模糊,rate = 0.6
+ float scale = param * length; // 0.6< length 返回0,反之返回1.
+ float alpha = param * (1.0 - abs(scale - 0.8) / 0.2); // 0.8 < length 返回0,反之返回1.
+ float param1 = step(length, 0.7); //小于0.5模糊
+ float scale1 = param1 * length; // 0.6< length 返回0,反之返回1.
+ alpha += param1 * (1.0 - abs(scale1 - 0.35) / 0.35); // 0.8 < length 返回0,反之返回1.
+ material.diffuse = color.rgb * vec3(color.a);
+ material.alpha = pow(alpha, 4.0);
+ return material;
+}
diff --git a/src/modules/material/shader/circle/CircleRotateMaterial.glsl b/src/modules/material/shader/circle/CircleRotateMaterial.glsl
new file mode 100644
index 00000000..e0afa57d
--- /dev/null
+++ b/src/modules/material/shader/circle/CircleRotateMaterial.glsl
@@ -0,0 +1,18 @@
+uniform vec4 color;
+uniform sampler2D image;
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ vec2 st = materialInput.st;
+ vec2 center = st - vec2(0.5,0.5);
+ float time = -czm_frameNumber * 3.1415926 / 180.;
+ float sin_t = sin(time);
+ float cos_t = cos(time);
+ vec2 center_rotate = vec2(center.s * cos_t - center.t * sin_t + 0.5,center.s * sin_t + center.t * cos_t + 0.5);
+ vec4 colorImage = texture(image,center_rotate);
+ vec3 temp = colorImage.rgb * color.rgb;
+ temp *= color.a;
+ material.diffuse = temp;
+ float length = 2. - length(center) / 0.5;
+ material.alpha = colorImage.a * pow(length, 0.5);
+ return material;
+}
diff --git a/src/modules/material/shader/circle/CircleScanMaterial.glsl b/src/modules/material/shader/circle/CircleScanMaterial.glsl
new file mode 100644
index 00000000..06270ffd
--- /dev/null
+++ b/src/modules/material/shader/circle/CircleScanMaterial.glsl
@@ -0,0 +1,26 @@
+uniform vec4 color;
+uniform float speed;
+
+float circle(vec2 uv, float r, float blur) {
+ float d = length(uv) * 2.0;
+ float c = smoothstep(r+blur, r, d);
+ return c;
+}
+
+czm_material czm_getMaterial(czm_materialInput materialInput)
+{
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ vec2 st = materialInput.st - .5;
+ material.diffuse = color.rgb;
+ material.emission = vec3(0);
+ float t =fract(czm_frameNumber * speed / 1000.0);
+ float s = 0.3;
+ float radius1 = smoothstep(.0, s, t) * 0.5;
+ float alpha1 = circle(st, radius1, 0.01) * circle(st, radius1, -0.01);
+ float alpha2 = circle(st, radius1, 0.01 - radius1) * circle(st, radius1, 0.01);
+ float radius2 = 0.5 + smoothstep(s, 1.0, t) * 0.5;
+ float alpha3 = circle(st, radius1, radius2 + 0.01 - radius1) * circle(st, radius1, -0.01);
+ material.alpha = smoothstep(1.0, s, t) * (alpha1 + alpha2*0.1 + alpha3*0.1);
+ material.alpha *= color.a;
+ return material;
+}
diff --git a/src/modules/material/shader/circle/CircleScanShader.glsl b/src/modules/material/shader/circle/CircleScanShader.glsl
new file mode 100644
index 00000000..274a0ba8
--- /dev/null
+++ b/src/modules/material/shader/circle/CircleScanShader.glsl
@@ -0,0 +1,46 @@
+in vec2 v_textureCoordinates;
+uniform sampler2D colorTexture;
+uniform sampler2D depthTexture;
+uniform vec3 centerWC;
+uniform vec3 normalWC;
+uniform float radius;
+uniform vec4 color;
+uniform float speed;
+
+float getDepth(){
+ float z_window = czm_unpackDepth(texture(depthTexture, v_textureCoordinates));
+ z_window = czm_reverseLogDepth(z_window);
+ float n_range = czm_depthRange.near;
+ float f_range = czm_depthRange.far;
+ return (2.0 * z_window - n_range - f_range) / (f_range - n_range);
+}
+
+vec4 toEye(in vec2 uv, in float depth){
+ vec2 xy = vec2((uv.x * 2.0 - 1.0),(uv.y * 2.0 - 1.0));
+ vec4 posInCamera =czm_inverseProjection * vec4(xy, depth, 1.0);
+ posInCamera = posInCamera / posInCamera.w;
+ return posInCamera;
+}
+
+vec3 pointProjectOnPlane(in vec3 planeNormal, in vec3 planeOrigin, in vec3 point){
+ vec3 v01 = point - planeOrigin;
+ float d = dot(planeNormal, v01) ;
+ return (point - planeNormal * d);
+}
+
+void main() {
+ out_FragColor = texture(colorTexture, v_textureCoordinates);
+ float depth = getDepth();
+ vec4 viewPos = toEye(v_textureCoordinates, depth);
+ vec4 center = czm_view * vec4(centerWC,1);
+ vec4 planeNormal = czm_view * vec4(normalWC,0);
+ vec3 prjOnPlane = pointProjectOnPlane(planeNormal.xyz, center.xyz, viewPos.xyz);
+ float dis = length(prjOnPlane.xyz - center.xyz);
+ float time = fract(czm_frameNumber * speed / 1000.0);
+ float temp = radius * time;
+ if(dis < temp) {
+ float f = 1.0 - abs(temp - dis) / temp;
+ f = pow(f, 4.0);
+ out_FragColor = mix(out_FragColor, color, f);
+ }
+}
diff --git a/src/modules/material/shader/circle/CircleSpiralMaterial.glsl b/src/modules/material/shader/circle/CircleSpiralMaterial.glsl
new file mode 100644
index 00000000..c9c060fa
--- /dev/null
+++ b/src/modules/material/shader/circle/CircleSpiralMaterial.glsl
@@ -0,0 +1,24 @@
+uniform vec4 color;
+uniform float speed;
+
+#define PI 3.14159265359
+
+vec2 rotate2D (vec2 _st, float _angle) {
+ _st = mat2(cos(_angle),-sin(_angle), sin(_angle),cos(_angle)) * _st;
+ return _st;
+}
+
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ vec2 st = materialInput.st * 2.0 - 1.0;
+ st *= 1.6;
+ float time = czm_frameNumber * speed / 1000.0;
+ float r = length(st);
+ float w = .3;
+ st = rotate2D(st,(r*PI*6.-time*2.));
+ float a = smoothstep(-w,.2,st.x) * smoothstep(w,.2,st.x);
+ float b = abs(1./(sin(pow(r,2.)*2.-time*1.3)*6.))*.4;
+ material.alpha = a * b ;
+ material.diffuse = color.rgb * a * b * 3.0;
+ return material;
+}
diff --git a/src/modules/material/shader/circle/CircleVaryMaterial.glsl b/src/modules/material/shader/circle/CircleVaryMaterial.glsl
new file mode 100644
index 00000000..f0c60264
--- /dev/null
+++ b/src/modules/material/shader/circle/CircleVaryMaterial.glsl
@@ -0,0 +1,18 @@
+uniform vec4 color;
+uniform float speed;
+
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ vec2 st = materialInput.st * 2.0 - 1.0;
+ float time =czm_frameNumber * speed / 1000.0;
+ float radius = length(st);
+ float angle = atan(st.y/st.x);
+ float radius1 = sin(time * 2.0) + sin(40.0*angle+time)*0.01;
+ float radius2 = cos(time * 3.0);
+ vec3 fragColor = 0.2 + 0.5 * cos( time + color.rgb + vec3(0,2,4));
+ float inten1 = 1.0 - sqrt(abs(radius1 - radius));
+ float inten2 = 1.0 - sqrt(abs(radius2 - radius));
+ material.alpha = pow(inten1 + inten2 , 5.0) ;
+ material.diffuse = fragColor * (inten1 + inten2);
+ return material;
+}
diff --git a/src/modules/material/shader/circle/CircleWaveMaterial.glsl b/src/modules/material/shader/circle/CircleWaveMaterial.glsl
new file mode 100644
index 00000000..f374bfa6
--- /dev/null
+++ b/src/modules/material/shader/circle/CircleWaveMaterial.glsl
@@ -0,0 +1,48 @@
+uniform vec4 color;
+uniform float speed;
+uniform float count;
+uniform float gradient;
+
+czm_material czm_getMaterial(czm_materialInput materialInput)
+{
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ material.diffuse = 1.5 * color.rgb;
+ vec2 st = materialInput.st;
+ float dis = distance(st, vec2(0.5, 0.5));
+ float per = fract(czm_frameNumber * speed / 1000.0);
+ if(count == 1.0){
+ if(dis > per * 0.5){
+ discard;
+ }else {
+ material.alpha = color.a * dis / per / 2.0;
+ }
+ } else {
+ vec3 str = materialInput.str;
+ if(abs(str.z) > 0.001){
+ discard;
+ }
+ if(dis > 0.5){
+ discard;
+ } else {
+ float perDis = 0.5 / count;
+ float disNum;
+ float bl = 0.0;
+ for(int i = 0; i <= 999; i++){
+ if(float(i) <= count){
+ disNum = perDis * float(i) - dis + per / count;
+ if(disNum > 0.0){
+ if(disNum < perDis){
+ bl = 1.0 - disNum / perDis;
+ }
+ else if(disNum - perDis < perDis){
+ bl = 1.0 - abs(1.0 - disNum / perDis);
+ }
+ material.alpha = pow(bl,(1.0 + 10.0 * (1.0 - gradient)));
+ }
+ }
+ }
+ }
+ }
+ return material;
+}
+
diff --git a/src/modules/material/shader/cylinder/CylinderFadeMaterial.glsl b/src/modules/material/shader/cylinder/CylinderFadeMaterial.glsl
new file mode 100644
index 00000000..3482ff96
--- /dev/null
+++ b/src/modules/material/shader/cylinder/CylinderFadeMaterial.glsl
@@ -0,0 +1,11 @@
+uniform vec4 color;
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ vec2 st = materialInput.st;
+ float powerRatio = 1. / (fract(czm_frameNumber / 30.0) + 1.) ;
+ float alpha = pow(1. - st.t,powerRatio);
+ vec4 temp = vec4(color.rgb, alpha * color.a);
+ material.diffuse = temp.rgb;
+ material.alpha = temp.a;
+ return material;
+}
diff --git a/src/modules/material/shader/cylinder/CylinderParticlesMaterial.glsl b/src/modules/material/shader/cylinder/CylinderParticlesMaterial.glsl
new file mode 100644
index 00000000..8db61cc0
--- /dev/null
+++ b/src/modules/material/shader/cylinder/CylinderParticlesMaterial.glsl
@@ -0,0 +1,16 @@
+uniform vec4 color;
+uniform sampler2D image;
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ vec2 st = materialInput.st;
+ float time = fract(czm_frameNumber / 90.) ;
+ vec2 new_st = fract(st- vec2(time,time));
+ vec4 colorImage = texture(image, new_st);
+ vec3 diffuse = colorImage.rgb;
+ float alpha = colorImage.a;
+ diffuse *= color.rgb;
+ alpha *= color.a;
+ material.diffuse = diffuse;
+ material.alpha = alpha * pow(1. - st.t,color.a);
+ return material;
+}
diff --git a/src/modules/material/shader/ellipsoid/EllipsoidElectricMaterial.glsl b/src/modules/material/shader/ellipsoid/EllipsoidElectricMaterial.glsl
new file mode 100644
index 00000000..090eeac1
--- /dev/null
+++ b/src/modules/material/shader/ellipsoid/EllipsoidElectricMaterial.glsl
@@ -0,0 +1,78 @@
+uniform vec4 color;
+uniform float speed;
+
+#define pi 3.1415926535
+#define PI2RAD 0.01745329252
+#define TWO_PI (2. * PI)
+
+float rands(float p){
+ return fract(sin(p) * 10000.0);
+}
+
+float noise(vec2 p){
+ float time = fract( czm_frameNumber * speed / 1000.0);
+ float t = time / 20000.0;
+ if(t > 1.0) t -= floor(t);
+ return rands(p.x * 14. + p.y * sin(t) * 0.5);
+}
+
+vec2 sw(vec2 p){
+ return vec2(floor(p.x), floor(p.y));
+}
+
+vec2 se(vec2 p){
+ return vec2(ceil(p.x), floor(p.y));
+}
+
+vec2 nw(vec2 p){
+ return vec2(floor(p.x), ceil(p.y));
+}
+
+vec2 ne(vec2 p){
+ return vec2(ceil(p.x), ceil(p.y));
+}
+
+float smoothNoise(vec2 p){
+ vec2 inter = smoothstep(0.0, 1.0, fract(p));
+ float s = mix(noise(sw(p)), noise(se(p)), inter.x);
+ float n = mix(noise(nw(p)), noise(ne(p)), inter.x);
+ return mix(s, n, inter.y);
+}
+
+float fbm(vec2 p){
+ float z = 2.0;
+ float rz = 0.0;
+ vec2 bp = p;
+ for(float i = 1.0; i < 6.0; i++){
+ rz += abs((smoothNoise(p) - 0.5)* 2.0) / z;
+ z *= 2.0;
+ p *= 2.0;
+ }
+ return rz;
+}
+
+czm_material czm_getMaterial(czm_materialInput materialInput)
+{
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ vec2 st = materialInput.st;
+ vec2 st2 = materialInput.st;
+ float time = fract( czm_frameNumber * speed / 1000.0);
+ if (st.t < 0.5) {
+ discard;
+ }
+ st *= 4.;
+ float rz = fbm(st);
+ st /= exp(mod( time * 2.0, pi));
+ rz *= pow(15., 0.9);
+ vec4 temp = vec4(0);
+ temp = mix( color / rz, vec4(color.rgb, 0.1), 0.2);
+ if (st2.s < 0.05) {
+ temp = mix(vec4(color.rgb, 0.1), temp, st2.s / 0.05);
+ }
+ if (st2.s > 0.95){
+ temp = mix(temp, vec4(color.rgb, 0.1), (st2.s - 0.95) / 0.05);
+ }
+ material.diffuse = temp.rgb;
+ material.alpha = temp.a * 2.0;
+ return material;
+}
diff --git a/src/modules/material/shader/ellipsoid/EllipsoidTrailMaterial.glsl b/src/modules/material/shader/ellipsoid/EllipsoidTrailMaterial.glsl
new file mode 100644
index 00000000..ca71386d
--- /dev/null
+++ b/src/modules/material/shader/ellipsoid/EllipsoidTrailMaterial.glsl
@@ -0,0 +1,12 @@
+uniform vec4 color;
+uniform float speed;
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ vec2 st = materialInput.st;
+ float time = fract(czm_frameNumber * speed / 1000.0);
+ float alpha = abs(smoothstep(0.5,1.,fract( -st.t - time)));
+ alpha += .1;
+ material.alpha = alpha;
+ material.diffuse = color.rgb;
+ return material;
+}
diff --git a/src/modules/material/shader/polyline/PolylineEmissionMaterial.glsl b/src/modules/material/shader/polyline/PolylineEmissionMaterial.glsl
new file mode 100644
index 00000000..01a87999
--- /dev/null
+++ b/src/modules/material/shader/polyline/PolylineEmissionMaterial.glsl
@@ -0,0 +1,11 @@
+uniform vec4 color;
+
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ vec4 fragColor = color;
+ fragColor = czm_gammaCorrect(fragColor);
+ material.emission = fragColor.rgb;
+ material.diffuse = fragColor.rgb;
+ material.alpha = color.a;
+ return material;
+}
diff --git a/src/modules/material/shader/polyline/PolylineFlickerMaterial.glsl b/src/modules/material/shader/polyline/PolylineFlickerMaterial.glsl
new file mode 100644
index 00000000..ee50d3f4
--- /dev/null
+++ b/src/modules/material/shader/polyline/PolylineFlickerMaterial.glsl
@@ -0,0 +1,11 @@
+uniform vec4 color;
+uniform float speed;
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ float time = fract( czm_frameNumber * speed / 1000.0);
+ vec2 st = materialInput.st;
+ float scalar = smoothstep(0.0,1.0,time);
+ material.diffuse = color.rgb * scalar;
+ material.alpha = color.a * scalar ;
+ return material;
+}
diff --git a/src/modules/material/shader/polyline/PolylineFlowMaterial.glsl b/src/modules/material/shader/polyline/PolylineFlowMaterial.glsl
new file mode 100644
index 00000000..861920b0
--- /dev/null
+++ b/src/modules/material/shader/polyline/PolylineFlowMaterial.glsl
@@ -0,0 +1,16 @@
+uniform vec4 color;
+uniform float speed;
+uniform float percent;
+uniform float gradient;
+
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ vec2 st = materialInput.st;
+ float t =fract(czm_frameNumber * speed / 1000.0);
+ t *= (1.0 + percent);
+ float alpha = smoothstep(t- percent, t, st.s) * step(-t, -st.s);
+ alpha += gradient;
+ material.diffuse = color.rgb;
+ material.alpha = alpha;
+ return material;
+}
diff --git a/src/modules/material/shader/polyline/PolylineImageTrailMaterial.glsl b/src/modules/material/shader/polyline/PolylineImageTrailMaterial.glsl
new file mode 100644
index 00000000..25681536
--- /dev/null
+++ b/src/modules/material/shader/polyline/PolylineImageTrailMaterial.glsl
@@ -0,0 +1,22 @@
+uniform sampler2D image;
+uniform float speed;
+uniform vec4 color;
+uniform vec2 repeat;
+
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ vec2 st = repeat * materialInput.st;
+ float time = fract(czm_frameNumber * speed / 1000.0);
+ vec4 colorImage = texture(image, vec2(fract(st.s - time), st.t));
+ if(color.a == 0.0){
+ if(colorImage.rgb == vec3(1.0) || colorImage.rgb == vec3(0.0)){
+ discard;
+ }
+ material.alpha = colorImage.a;
+ material.diffuse = colorImage.rgb;
+ }else{
+ material.alpha = colorImage.a * color.a;
+ material.diffuse = max(color.rgb * material.alpha * 3.0, color.rgb);
+ }
+ return material;
+}
diff --git a/src/modules/material/shader/polyline/PolylineLightingMaterial.glsl b/src/modules/material/shader/polyline/PolylineLightingMaterial.glsl
new file mode 100644
index 00000000..34645eed
--- /dev/null
+++ b/src/modules/material/shader/polyline/PolylineLightingMaterial.glsl
@@ -0,0 +1,11 @@
+uniform sampler2D image;
+uniform vec4 color;
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ vec2 st = materialInput.st;
+ vec4 colorImage = texture(image,st);
+ vec3 fragColor = color.rgb;
+ material.alpha = colorImage.a * color.a * 3.;
+ material.diffuse = max(fragColor.rgb + colorImage.rgb , fragColor.rgb);
+ return material;
+}
diff --git a/src/modules/material/shader/polyline/PolylineLightingTrailMaterial.glsl b/src/modules/material/shader/polyline/PolylineLightingTrailMaterial.glsl
new file mode 100644
index 00000000..4449e7d2
--- /dev/null
+++ b/src/modules/material/shader/polyline/PolylineLightingTrailMaterial.glsl
@@ -0,0 +1,21 @@
+uniform sampler2D image;
+uniform vec4 color;
+uniform float speed;
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ vec2 st = materialInput.st;
+ float time = fract(czm_frameNumber * speed / 1000.0);
+ vec4 colorImage = texture(image,st);
+ vec3 fragColor = color.rgb;
+ if(st.t > 0.45 && st.t < 0.55 ) {
+ fragColor = vec3(1.0);
+ }
+ if(color.a == 0.0){
+ material.alpha = colorImage.a * 1.5 * fract(st.s - time);
+ material.diffuse = colorImage.rgb;
+ }else{
+ material.alpha = colorImage.a * color.a * 1.5 * smoothstep(.0,1., fract(st.s - time));
+ material.diffuse = max(fragColor.rgb * material.alpha , fragColor.rgb);
+ }
+ return material;
+}
diff --git a/src/modules/material/shader/polyline/PolylineTrailMaterial.glsl b/src/modules/material/shader/polyline/PolylineTrailMaterial.glsl
new file mode 100644
index 00000000..50d29783
--- /dev/null
+++ b/src/modules/material/shader/polyline/PolylineTrailMaterial.glsl
@@ -0,0 +1,11 @@
+uniform vec4 color;
+uniform float speed;
+
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ vec2 st = materialInput.st;
+ float time = fract(czm_frameNumber * speed / 1000.0);
+ material.diffuse = color.rgb;
+ material.alpha = color.a * fract(st.s-time);
+ return material;
+}
diff --git a/src/modules/material/shader/radar/RadarLineMaterial.glsl b/src/modules/material/shader/radar/RadarLineMaterial.glsl
new file mode 100644
index 00000000..1301017b
--- /dev/null
+++ b/src/modules/material/shader/radar/RadarLineMaterial.glsl
@@ -0,0 +1,24 @@
+uniform vec4 color;
+uniform float speed;
+
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ vec2 st = materialInput.st * 2.0 - 1.0;
+ float t = czm_frameNumber * speed / 1000.0 ;
+ vec3 col = vec3(0.0);
+ vec2 p = vec2(sin(t), cos(t));
+ float d = length(st - dot(p, st) * p);
+ if (dot(st, p) < 0.) {
+ d = length(st);
+ }
+
+ col = .006 / d * color.rgb;
+
+ if(distance(st,vec2(0)) > 0.99 ){
+ col =color.rgb;
+ }
+
+ material.alpha = pow(length(col),2.0);
+ material.diffuse = col * 3.0 ;
+ return material;
+}
diff --git a/src/modules/material/shader/radar/RadarScanShader.glsl b/src/modules/material/shader/radar/RadarScanShader.glsl
new file mode 100644
index 00000000..cb1142df
--- /dev/null
+++ b/src/modules/material/shader/radar/RadarScanShader.glsl
@@ -0,0 +1,70 @@
+in vec2 v_textureCoordinates;
+uniform sampler2D colorTexture;
+uniform sampler2D depthTexture;
+uniform vec3 centerWC;
+uniform vec3 planeNormalWC;
+uniform vec3 lineNormalWC;
+uniform float radius;
+uniform vec4 color;
+
+float getDepth(){
+ float z_window = czm_unpackDepth(texture(depthTexture, v_textureCoordinates));
+ z_window = czm_reverseLogDepth(z_window);
+ float n_range = czm_depthRange.near;
+ float f_range = czm_depthRange.far;
+ return (2.0 * z_window - n_range - f_range) / (f_range - n_range);
+}
+
+vec4 toEye(in vec2 uv, in float depth){
+ vec2 xy = vec2((uv.x * 2.0 - 1.0),(uv.y * 2.0 - 1.0));
+ vec4 posInCamera =czm_inverseProjection * vec4(xy, depth, 1.0);
+ posInCamera = posInCamera / posInCamera.w;
+ return posInCamera;
+}
+
+bool isPointOnLineRight(in vec3 ptOnLine, in vec3 lineNormal, in vec3 testPt)
+{
+ vec3 v01 = testPt - ptOnLine;
+ normalize(v01);
+ vec3 temp = cross(v01, lineNormal);
+ vec4 planeNormalEC = czm_view * vec4(planeNormalWC,0);
+ float d = dot(temp, planeNormalEC.xyz);
+ return d > 0.5;
+}
+
+vec3 pointProjectOnPlane(in vec3 planeNormal, in vec3 planeOrigin, in vec3 point)
+{
+ vec3 v01 = point -planeOrigin;
+ float d = dot(planeNormal, v01) ;
+ return (point - planeNormal * d);
+}
+
+float distancePointToLine(in vec3 ptOnLine, in vec3 lineNormal, in vec3 testPt)
+{
+ vec3 tempPt = pointProjectOnPlane(lineNormal, ptOnLine, testPt);
+ return length(tempPt - ptOnLine);
+}
+
+void main() {
+ out_FragColor = texture(colorTexture, v_textureCoordinates);
+ float depth = getDepth();
+ vec4 viewPos = toEye(v_textureCoordinates, depth);
+ vec4 centerEC = czm_view * vec4(centerWC,1);
+ vec4 planeNormalEC = czm_view * vec4(planeNormalWC,0);
+ vec4 lineNormalEC = czm_view * vec4(lineNormalWC,0);
+ vec3 prjOnPlane = pointProjectOnPlane(planeNormalEC.xyz, centerEC.xyz, viewPos.xyz);
+ float dis = length(prjOnPlane.xyz - centerEC.xyz);
+ float diameter = radius * 2.0;
+ if(dis < radius){
+ float f0 = 1.0 -abs(radius - dis) / radius;
+ f0 = pow(f0, 64.0);
+ vec3 lineEndPt = vec3(centerEC.xyz) + vec3(lineNormalEC.xyz) * radius;
+ float f = 0.0;
+ if(isPointOnLineRight(centerEC.xyz, lineNormalEC.xyz, prjOnPlane.xyz)) {
+ float dis1= length(prjOnPlane.xyz - lineEndPt);
+ f = abs(diameter - dis1) / diameter;
+ f = pow(f, 3.0);
+ }
+ out_FragColor = mix(out_FragColor, color, f + f0);
+ }
+}
diff --git a/src/modules/material/shader/radar/RadarSweepMaterial.glsl b/src/modules/material/shader/radar/RadarSweepMaterial.glsl
new file mode 100644
index 00000000..50468fdc
--- /dev/null
+++ b/src/modules/material/shader/radar/RadarSweepMaterial.glsl
@@ -0,0 +1,32 @@
+uniform vec4 color;
+uniform float speed;
+
+#define PI 3.14159265359
+
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ vec2 st = materialInput.st;
+ vec2 scrPt = st * 2.0 - 1.0;
+ float time = czm_frameNumber * speed / 1000.0 ;
+ vec3 col = vec3(0.0);
+ mat2 rot;
+ float theta = -time * 1.0 * PI - 2.2;
+ float cosTheta, sinTheta;
+ cosTheta = cos(theta);
+ sinTheta = sin(theta);
+ rot[0][0] = cosTheta;
+ rot[0][1] = -sinTheta;
+ rot[1][0] = sinTheta;
+ rot[1][1] = cosTheta;
+ vec2 scrPtRot = rot * scrPt;
+ float angle = 1.0 - (atan(scrPtRot.y, scrPtRot.x) / 6.2831 + 0.5);
+ float falloff = 1.0 - length(scrPtRot);
+ float ringSpacing = 0.23;
+ if(mod(length(scrPtRot), ringSpacing) < 0.015 && length(scrPtRot) / ringSpacing < 5.0) {
+ col += vec3(0, 0.5, 0);
+ }
+ col += vec3(0, 0.8, 0) * step(mod(length(scrPtRot), ringSpacing), 0.01) * step(length(scrPtRot), 1.0);
+ material.alpha =pow(length(col + vec3(.5)),5.0);
+ material.diffuse = (0.5 + pow(angle, 2.0) * falloff ) * color.rgb ;
+ return material;
+}
diff --git a/src/modules/material/shader/radar/RadarWaveMaterial.glsl b/src/modules/material/shader/radar/RadarWaveMaterial.glsl
new file mode 100644
index 00000000..313bd218
--- /dev/null
+++ b/src/modules/material/shader/radar/RadarWaveMaterial.glsl
@@ -0,0 +1,27 @@
+uniform vec4 color;
+uniform float speed;
+
+#define PI 3.14159265359
+
+float rand(vec2 co){
+ return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
+}
+
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ vec2 st = materialInput.st;
+ vec2 pos = st - vec2(0.5);
+ float time = czm_frameNumber * speed / 1000.0 ;
+ float r = length(pos);
+ float t = atan(pos.y, pos.x) - time * 2.5;
+ float a = (atan(sin(t), cos(t)) + PI)/(2.0*PI);
+ float ta = 0.5;
+ float v = smoothstep(ta-0.05,ta+0.05,a) * smoothstep(ta+0.05,ta-0.05,a);
+ vec3 flagColor = color.rgb * v;
+ float blink = pow(sin(time*1.5)*0.5+0.5, 0.8);
+ flagColor = color.rgb * pow(a, 8.0*(.2+blink))*(sin(r*500.0)*.5+.5) ;
+ flagColor = flagColor * pow(r, 0.4);
+ material.alpha = length(flagColor) * 1.3;
+ material.diffuse = flagColor * 3.0;
+ return material;
+}
diff --git a/src/modules/material/shader/thirdpart/AsphaltMaterial.glsl b/src/modules/material/shader/thirdpart/AsphaltMaterial.glsl
new file mode 100644
index 00000000..405f956a
--- /dev/null
+++ b/src/modules/material/shader/thirdpart/AsphaltMaterial.glsl
@@ -0,0 +1,24 @@
+uniform vec4 asphaltColor;
+uniform float bumpSize;
+uniform float roughness;
+
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+
+ // From Stefan Gustavson's Procedural Textures in GLSL in OpenGL Insights
+ //Main cellular pattern
+ vec4 color = asphaltColor;
+ vec2 st = materialInput.st;
+ vec2 F = czm_cellular(st / bumpSize);
+ color.rgb -= (F.x / F.y) * 0.1;
+
+ //Extra bumps for roughness
+ float noise = czm_snoise(st / bumpSize);
+ noise = pow(noise, 5.0) * roughness;
+ color.rgb += noise;
+
+ material.diffuse = color.rgb;
+ material.alpha = color.a;
+
+ return material;
+}
diff --git a/src/modules/material/shader/thirdpart/BlobMaterial.glsl b/src/modules/material/shader/thirdpart/BlobMaterial.glsl
new file mode 100644
index 00000000..8d302c49
--- /dev/null
+++ b/src/modules/material/shader/thirdpart/BlobMaterial.glsl
@@ -0,0 +1,17 @@
+uniform vec4 lightColor;
+uniform vec4 darkColor;
+uniform float frequency;
+
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+
+ // From Stefan Gustavson's Procedural Textures in GLSL in OpenGL Insights
+ vec2 F = czm_cellular(materialInput.st * frequency);
+ float t = 1.0 - F.x * F.x;
+
+ vec4 color = mix(lightColor, darkColor, t);
+ material.diffuse = color.rgb;
+ material.alpha = color.a;
+
+ return material;
+}
diff --git a/src/modules/material/shader/thirdpart/BrickMaterial.glsl b/src/modules/material/shader/thirdpart/BrickMaterial.glsl
new file mode 100644
index 00000000..91d9c526
--- /dev/null
+++ b/src/modules/material/shader/thirdpart/BrickMaterial.glsl
@@ -0,0 +1,41 @@
+uniform vec4 brickColor;
+uniform vec4 mortarColor;
+uniform vec2 brickSize;
+uniform vec2 brickPct;
+uniform float brickRoughness;
+uniform float mortarRoughness;
+
+#define Integral(x, p) ((floor(x) * p) + max(fract(x) - (1.0 - p), 0.0))
+
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+
+ // From OpenGL Shading Language (3rd edition) pg. 194, 501
+ vec2 st = materialInput.st;
+ vec2 position = st / brickSize;
+ if(fract(position.y * 0.5) > 0.5) {
+ position.x += 0.5;
+ }
+
+ //calculate whether to use brick or mortar (does AA)
+ vec2 filterWidth = vec2(0.02);
+ vec2 useBrick = (Integral(position + filterWidth, brickPct) -
+ Integral(position, brickPct)) / filterWidth;
+ float useBrickFinal = useBrick.x * useBrick.y;
+ vec4 color = mix(mortarColor, brickColor, useBrickFinal);
+
+ //Apply noise to brick
+ vec2 brickScaled = vec2(st.x / 0.1, st.y / 0.006);
+ float brickNoise = abs(czm_snoise(brickScaled) * brickRoughness / 5.0);
+ color.rg += brickNoise * useBrickFinal;
+
+ //Apply noise to mortar
+ vec2 mortarScaled = st / 0.005;
+ float mortarNoise = max(czm_snoise(mortarScaled) * mortarRoughness, 0.0);
+ color.rgb += mortarNoise * (1.0 - useBrickFinal);
+
+ material.diffuse = color.rgb;
+ material.alpha = color.a;
+
+ return material;
+}
diff --git a/src/modules/material/shader/thirdpart/CementMaterial.glsl b/src/modules/material/shader/thirdpart/CementMaterial.glsl
new file mode 100644
index 00000000..a356509a
--- /dev/null
+++ b/src/modules/material/shader/thirdpart/CementMaterial.glsl
@@ -0,0 +1,18 @@
+uniform vec4 cementColor;
+uniform float grainScale;
+uniform float roughness;
+
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+
+ float noise = czm_snoise(materialInput.st / grainScale);
+ noise = pow(noise, 5.0) * roughness;
+
+ vec4 color = cementColor;
+ color.rgb += noise;
+
+ material.diffuse = color.rgb;
+ material.alpha = color.a;
+
+ return material;
+}
diff --git a/src/modules/material/shader/thirdpart/ErosionMaterial.glsl b/src/modules/material/shader/thirdpart/ErosionMaterial.glsl
new file mode 100644
index 00000000..81449591
--- /dev/null
+++ b/src/modules/material/shader/thirdpart/ErosionMaterial.glsl
@@ -0,0 +1,21 @@
+uniform vec4 color;
+uniform float time;
+
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ float alpha = 1.0;
+ if (time != 1.0)
+ {
+ float t = 0.5 + (0.5 * czm_snoise(materialInput.str / (1.0 / 10.0))); // Scale [-1, 1] to [0, 1]
+
+ if (t > time)
+ {
+ alpha = 0.0;
+ }
+ }
+
+ material.diffuse = color.rgb;
+ material.alpha = color.a * alpha;
+
+ return material;
+}
diff --git a/src/modules/material/shader/thirdpart/FacetMaterial.glsl b/src/modules/material/shader/thirdpart/FacetMaterial.glsl
new file mode 100644
index 00000000..b6268e93
--- /dev/null
+++ b/src/modules/material/shader/thirdpart/FacetMaterial.glsl
@@ -0,0 +1,17 @@
+uniform vec4 lightColor;
+uniform vec4 darkColor;
+uniform float frequency;
+
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+
+ // From Stefan Gustavson's Procedural Textures in GLSL in OpenGL Insights
+ vec2 F = czm_cellular(materialInput.st * frequency);
+ float t = 0.1 + (F.y - F.x);
+
+ vec4 color = mix(lightColor, darkColor, t);
+ material.diffuse = color.rgb;
+ material.alpha = color.a;
+
+ return material;
+}
diff --git a/src/modules/material/shader/thirdpart/FresnelMaterial.glsl b/src/modules/material/shader/thirdpart/FresnelMaterial.glsl
new file mode 100644
index 00000000..acd9efcf
--- /dev/null
+++ b/src/modules/material/shader/thirdpart/FresnelMaterial.glsl
@@ -0,0 +1,11 @@
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+
+ vec3 normalWC = normalize(czm_inverseViewRotation * material.normal);
+ vec3 positionWC = normalize(czm_inverseViewRotation * materialInput.positionToEyeEC);
+ float cosAngIncidence = max(dot(normalWC, positionWC), 0.0);
+
+ material.diffuse = mix(reflection.diffuse, refraction.diffuse, cosAngIncidence);
+
+ return material;
+}
diff --git a/src/modules/material/shader/thirdpart/GrassMaterial.glsl b/src/modules/material/shader/thirdpart/GrassMaterial.glsl
new file mode 100644
index 00000000..0238d832
--- /dev/null
+++ b/src/modules/material/shader/thirdpart/GrassMaterial.glsl
@@ -0,0 +1,27 @@
+uniform vec4 grassColor;
+uniform vec4 dirtColor;
+uniform float patchiness;
+
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+
+ vec2 st = materialInput.st;
+ float noise1 = (czm_snoise(st * patchiness * 1.0)) * 1.0;
+ float noise2 = (czm_snoise(st * patchiness * 2.0)) * 0.5;
+ float noise3 = (czm_snoise(st * patchiness * 4.0)) * 0.25;
+ float noise = sin(noise1 + noise2 + noise3) * 0.1;
+
+ vec4 color = mix(grassColor, dirtColor, noise);
+
+ //Make thatch patterns
+ float verticalNoise = czm_snoise(vec2(st.x * 100.0, st.y * 20.0)) * 0.02;
+ float horizontalNoise = czm_snoise(vec2(st.x * 20.0, st.y * 100.0)) * 0.02;
+ float stripeNoise = min(verticalNoise, horizontalNoise);
+
+ color.rgb += stripeNoise;
+
+ material.diffuse = color.rgb;
+ material.alpha = color.a;
+
+ return material;
+}
diff --git a/src/modules/material/shader/thirdpart/ReflectionMaterial.glsl b/src/modules/material/shader/thirdpart/ReflectionMaterial.glsl
new file mode 100644
index 00000000..d5c1ead0
--- /dev/null
+++ b/src/modules/material/shader/thirdpart/ReflectionMaterial.glsl
@@ -0,0 +1,12 @@
+uniform samplerCube cubeMap;
+
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+
+ vec3 normalWC = normalize(czm_inverseViewRotation * material.normal);
+ vec3 positionWC = normalize(czm_inverseViewRotation * materialInput.positionToEyeEC);
+ vec3 reflectedWC = reflect(positionWC, normalWC);
+ material.diffuse = textureCube(cubeMap, reflectedWC).channels;
+
+ return material;
+}
diff --git a/src/modules/material/shader/thirdpart/RefractionMaterial.glsl b/src/modules/material/shader/thirdpart/RefractionMaterial.glsl
new file mode 100644
index 00000000..92cf34ba
--- /dev/null
+++ b/src/modules/material/shader/thirdpart/RefractionMaterial.glsl
@@ -0,0 +1,13 @@
+uniform samplerCube cubeMap;
+uniform float indexOfRefractionRatio;
+
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+
+ vec3 normalWC = normalize(czm_inverseViewRotation * material.normal);
+ vec3 positionWC = normalize(czm_inverseViewRotation * materialInput.positionToEyeEC);
+ vec3 refractedWC = refract(positionWC, -normalWC, indexOfRefractionRatio);
+ material.diffuse = textureCube(cubeMap, refractedWC).channels;
+
+ return material;
+}
diff --git a/src/modules/material/shader/thirdpart/TieDyeMaterial.glsl b/src/modules/material/shader/thirdpart/TieDyeMaterial.glsl
new file mode 100644
index 00000000..23d23dd5
--- /dev/null
+++ b/src/modules/material/shader/thirdpart/TieDyeMaterial.glsl
@@ -0,0 +1,16 @@
+uniform vec4 lightColor;
+uniform vec4 darkColor;
+uniform float frequency;
+
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+
+ vec3 scaled = materialInput.str * frequency;
+ float t = abs(czm_snoise(scaled));
+
+ vec4 color = mix(lightColor, darkColor, t);
+ material.diffuse = color.rgb;
+ material.alpha = color.a;
+
+ return material;
+}
diff --git a/src/modules/material/shader/thirdpart/WoodMaterial.glsl b/src/modules/material/shader/thirdpart/WoodMaterial.glsl
new file mode 100644
index 00000000..00e54698
--- /dev/null
+++ b/src/modules/material/shader/thirdpart/WoodMaterial.glsl
@@ -0,0 +1,35 @@
+uniform vec4 lightWoodColor;
+uniform vec4 darkWoodColor;
+uniform float ringFrequency;
+uniform vec2 noiseScale;
+uniform float grainFrequency;
+
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+
+ //Based on wood shader from OpenGL Shading Language (3rd edition) pg. 455
+ vec2 st = materialInput.st;
+
+ vec2 noisevec;
+ noisevec.x = czm_snoise(st * noiseScale.x);
+ noisevec.y = czm_snoise(st * noiseScale.y);
+
+ vec2 location = st + noisevec;
+ float dist = sqrt(location.x * location.x + location.y * location.y);
+ dist *= ringFrequency;
+
+ float r = fract(dist + noisevec[0] + noisevec[1]) * 2.0;
+ if(r > 1.0)
+ r = 2.0 - r;
+
+ vec4 color = mix(lightWoodColor, darkWoodColor, r);
+
+ //streaks
+ r = abs(czm_snoise(vec2(st.x * grainFrequency, st.y * grainFrequency * 0.02))) * 0.2;
+ color.rgb += lightWoodColor.rgb * r;
+
+ material.diffuse = color.rgb;
+ material.alpha = color.a;
+
+ return material;
+}
diff --git a/src/modules/material/shader/thirdpart/cellular.glsl b/src/modules/material/shader/thirdpart/cellular.glsl
new file mode 100644
index 00000000..946eeb1d
--- /dev/null
+++ b/src/modules/material/shader/thirdpart/cellular.glsl
@@ -0,0 +1,77 @@
+/**
+ * @license
+ * Cellular noise ("Worley noise") in 2D in GLSL.
+ * Copyright (c) Stefan Gustavson 2011-04-19. All rights reserved.
+ * This code is released under the conditions of the MIT license.
+ * See LICENSE file for details.
+ */
+
+//#ifdef GL_OES_standard_derivatives
+// #extension GL_OES_standard_derivatives : enable
+//#endif
+//
+//float aastep (float threshold , float value)
+//{
+// float afwidth = 0.7 * length ( vec2 ( dFdx ( value ), dFdy ( value )));
+// return smoothstep ( threshold - afwidth , threshold + afwidth , value );
+//}
+
+// Permutation polynomial: (34x^2 + x) mod 289
+vec3 _czm_permute289(vec3 x)
+{
+ return mod((34.0 * x + 1.0) * x, 289.0);
+}
+
+/**
+ * DOC_TBA
+ *
+ * Implemented by Stefan Gustavson, and distributed under the MIT License. {@link http://openglinsights.git.sourceforge.net/git/gitweb.cgi?p=openglinsights/openglinsights;a=tree;f=proceduraltextures}
+ *
+ * @name czm_cellular
+ * @glslFunction
+ *
+ * @see Stefan Gustavson's chapter, Procedural Textures in GLSL , in OpenGL Insights .
+ */
+vec2 czm_cellular(vec2 P)
+// Cellular noise, returning F1 and F2 in a vec2.
+// Standard 3x3 search window for good F1 and F2 values
+{
+#define K 0.142857142857 // 1/7
+#define Ko 0.428571428571 // 3/7
+#define jitter 1.0 // Less gives more regular pattern
+ vec2 Pi = mod(floor(P), 289.0);
+ vec2 Pf = fract(P);
+ vec3 oi = vec3(-1.0, 0.0, 1.0);
+ vec3 of = vec3(-0.5, 0.5, 1.5);
+ vec3 px = _czm_permute289(Pi.x + oi);
+ vec3 p = _czm_permute289(px.x + Pi.y + oi); // p11, p12, p13
+ vec3 ox = fract(p*K) - Ko;
+ vec3 oy = mod(floor(p*K),7.0)*K - Ko;
+ vec3 dx = Pf.x + 0.5 + jitter*ox;
+ vec3 dy = Pf.y - of + jitter*oy;
+ vec3 d1 = dx * dx + dy * dy; // d11, d12 and d13, squared
+ p = _czm_permute289(px.y + Pi.y + oi); // p21, p22, p23
+ ox = fract(p*K) - Ko;
+ oy = mod(floor(p*K),7.0)*K - Ko;
+ dx = Pf.x - 0.5 + jitter*ox;
+ dy = Pf.y - of + jitter*oy;
+ vec3 d2 = dx * dx + dy * dy; // d21, d22 and d23, squared
+ p = _czm_permute289(px.z + Pi.y + oi); // p31, p32, p33
+ ox = fract(p*K) - Ko;
+ oy = mod(floor(p*K),7.0)*K - Ko;
+ dx = Pf.x - 1.5 + jitter*ox;
+ dy = Pf.y - of + jitter*oy;
+ vec3 d3 = dx * dx + dy * dy; // d31, d32 and d33, squared
+ // Sort out the two smallest distances (F1, F2)
+ vec3 d1a = min(d1, d2);
+ d2 = max(d1, d2); // Swap to keep candidates for F2
+ d2 = min(d2, d3); // neither F1 nor F2 are now in d3
+ d1 = min(d1a, d2); // F1 is now in d1
+ d2 = max(d1a, d2); // Swap to keep candidates for F2
+ d1.xy = (d1.x < d1.y) ? d1.xy : d1.yx; // Swap if smaller
+ d1.xz = (d1.x < d1.z) ? d1.xz : d1.zx; // F1 is in d1.x
+ d1.yz = min(d1.yz, d2.yz); // F2 is now not in d2.yz
+ d1.y = min(d1.y, d1.z); // nor in d1.z
+ d1.y = min(d1.y, d2.x); // F2 is in d1.y, we're done.
+ return sqrt(d1.xy);
+}
diff --git a/src/modules/material/shader/thirdpart/snoise.glsl b/src/modules/material/shader/thirdpart/snoise.glsl
new file mode 100644
index 00000000..27e9603b
--- /dev/null
+++ b/src/modules/material/shader/thirdpart/snoise.glsl
@@ -0,0 +1,282 @@
+/**
+ * @license
+ * Description : Array and textureless GLSL 2D/3D/4D simplex
+ * noise functions.
+ * Author : Ian McEwan, Ashima Arts.
+ * Maintainer : ijm
+ * Lastmod : 20110822 (ijm)
+ * License : Copyright (C) 2011 Ashima Arts. All rights reserved.
+ * Distributed under the MIT License. See LICENSE file.
+ * https://github.com/ashima/webgl-noise
+ */
+
+vec4 _czm_mod289(vec4 x)
+{
+ return x - floor(x * (1.0 / 289.0)) * 289.0;
+}
+
+vec3 _czm_mod289(vec3 x)
+{
+ return x - floor(x * (1.0 / 289.0)) * 289.0;
+}
+
+vec2 _czm_mod289(vec2 x)
+{
+ return x - floor(x * (1.0 / 289.0)) * 289.0;
+}
+
+float _czm_mod289(float x)
+{
+ return x - floor(x * (1.0 / 289.0)) * 289.0;
+}
+
+vec4 _czm_permute(vec4 x)
+{
+ return _czm_mod289(((x*34.0)+1.0)*x);
+}
+
+vec3 _czm_permute(vec3 x)
+{
+ return _czm_mod289(((x*34.0)+1.0)*x);
+}
+
+float _czm_permute(float x)
+{
+ return _czm_mod289(((x*34.0)+1.0)*x);
+}
+
+vec4 _czm_taylorInvSqrt(vec4 r)
+{
+ return 1.79284291400159 - 0.85373472095314 * r;
+}
+
+float _czm_taylorInvSqrt(float r)
+{
+ return 1.79284291400159 - 0.85373472095314 * r;
+}
+
+vec4 _czm_grad4(float j, vec4 ip)
+{
+ const vec4 ones = vec4(1.0, 1.0, 1.0, -1.0);
+ vec4 p,s;
+
+ p.xyz = floor( fract (vec3(j) * ip.xyz) * 7.0) * ip.z - 1.0;
+ p.w = 1.5 - dot(abs(p.xyz), ones.xyz);
+ s = vec4(lessThan(p, vec4(0.0)));
+ p.xyz = p.xyz + (s.xyz*2.0 - 1.0) * s.www;
+
+ return p;
+}
+
+/**
+ * DOC_TBA
+ *
+ * Implemented by Ian McEwan, Ashima Arts, and distributed under the MIT License. {@link https://github.com/ashima/webgl-noise}
+ *
+ * @name czm_snoise
+ * @glslFunction
+ *
+ * @see https://github.com/ashima/webgl-noise
+ * @see Stefan Gustavson's paper Simplex noise demystified
+ */
+float czm_snoise(vec2 v)
+{
+ const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0
+ 0.366025403784439, // 0.5*(sqrt(3.0)-1.0)
+ -0.577350269189626, // -1.0 + 2.0 * C.x
+ 0.024390243902439); // 1.0 / 41.0
+ // First corner
+ vec2 i = floor(v + dot(v, C.yy) );
+ vec2 x0 = v - i + dot(i, C.xx);
+
+ // Other corners
+ vec2 i1;
+ //i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0
+ //i1.y = 1.0 - i1.x;
+ i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
+ // x0 = x0 - 0.0 + 0.0 * C.xx ;
+ // x1 = x0 - i1 + 1.0 * C.xx ;
+ // x2 = x0 - 1.0 + 2.0 * C.xx ;
+ vec4 x12 = x0.xyxy + C.xxzz;
+ x12.xy -= i1;
+
+ // Permutations
+ i = _czm_mod289(i); // Avoid truncation effect in permutation
+ vec3 p = _czm_permute( _czm_permute( i.y + vec3(0.0, i1.y, 1.0 )) + i.x + vec3(0.0, i1.x, 1.0 ));
+
+ vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
+ m = m*m ;
+ m = m*m ;
+
+ // Gradients: 41 points uniformly over a line, mapped onto a diamond.
+ // The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)
+ vec3 x = 2.0 * fract(p * C.www) - 1.0;
+ vec3 h = abs(x) - 0.5;
+ vec3 ox = floor(x + 0.5);
+ vec3 a0 = x - ox;
+
+ // Normalise gradients implicitly by scaling m
+ // Approximation of: m *= inversesqrt( a0*a0 + h*h );
+ m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );
+
+ // Compute final noise value at P
+ vec3 g;
+ g.x = a0.x * x0.x + h.x * x0.y;
+ g.yz = a0.yz * x12.xz + h.yz * x12.yw;
+ return 130.0 * dot(m, g);
+}
+
+float czm_snoise(vec3 v)
+{
+ const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;
+ const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
+
+ // First corner
+ vec3 i = floor(v + dot(v, C.yyy) );
+ vec3 x0 = v - i + dot(i, C.xxx) ;
+
+ // Other corners
+ vec3 g = step(x0.yzx, x0.xyz);
+ vec3 l = 1.0 - g;
+ vec3 i1 = min( g.xyz, l.zxy );
+ vec3 i2 = max( g.xyz, l.zxy );
+
+ // x0 = x0 - 0.0 + 0.0 * C.xxx;
+ // x1 = x0 - i1 + 1.0 * C.xxx;
+ // x2 = x0 - i2 + 2.0 * C.xxx;
+ // x3 = x0 - 1.0 + 3.0 * C.xxx;
+ vec3 x1 = x0 - i1 + C.xxx;
+ vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
+ vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y
+
+ // Permutations
+ i = _czm_mod289(i);
+ vec4 p = _czm_permute( _czm_permute( _czm_permute(
+ i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
+ + i.y + vec4(0.0, i1.y, i2.y, 1.0 ))
+ + i.x + vec4(0.0, i1.x, i2.x, 1.0 ));
+
+ // Gradients: 7x7 points over a square, mapped onto an octahedron.
+ // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
+ float n_ = 0.142857142857; // 1.0/7.0
+ vec3 ns = n_ * D.wyz - D.xzx;
+
+ vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)
+
+ vec4 x_ = floor(j * ns.z);
+ vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N)
+
+ vec4 x = x_ *ns.x + ns.yyyy;
+ vec4 y = y_ *ns.x + ns.yyyy;
+ vec4 h = 1.0 - abs(x) - abs(y);
+
+ vec4 b0 = vec4( x.xy, y.xy );
+ vec4 b1 = vec4( x.zw, y.zw );
+
+ //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;
+ //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;
+ vec4 s0 = floor(b0)*2.0 + 1.0;
+ vec4 s1 = floor(b1)*2.0 + 1.0;
+ vec4 sh = -step(h, vec4(0.0));
+
+ vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;
+ vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;
+
+ vec3 p0 = vec3(a0.xy,h.x);
+ vec3 p1 = vec3(a0.zw,h.y);
+ vec3 p2 = vec3(a1.xy,h.z);
+ vec3 p3 = vec3(a1.zw,h.w);
+
+ //Normalise gradients
+ vec4 norm = _czm_taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
+ p0 *= norm.x;
+ p1 *= norm.y;
+ p2 *= norm.z;
+ p3 *= norm.w;
+
+ // Mix final noise value
+ vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
+ m = m * m;
+ return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),
+ dot(p2,x2), dot(p3,x3) ) );
+}
+
+float czm_snoise(vec4 v)
+{
+ const vec4 C = vec4( 0.138196601125011, // (5 - sqrt(5))/20 G4
+ 0.276393202250021, // 2 * G4
+ 0.414589803375032, // 3 * G4
+ -0.447213595499958); // -1 + 4 * G4
+
+ // (sqrt(5) - 1)/4 = F4, used once below
+ #define F4 0.309016994374947451
+
+ // First corner
+ vec4 i = floor(v + dot(v, vec4(F4)) );
+ vec4 x0 = v - i + dot(i, C.xxxx);
+
+ // Other corners
+
+ // Rank sorting originally contributed by Bill Licea-Kane, AMD (formerly ATI)
+ vec4 i0;
+ vec3 isX = step( x0.yzw, x0.xxx );
+ vec3 isYZ = step( x0.zww, x0.yyz );
+ // i0.x = dot( isX, vec3( 1.0 ) );
+ i0.x = isX.x + isX.y + isX.z;
+ i0.yzw = 1.0 - isX;
+ // i0.y += dot( isYZ.xy, vec2( 1.0 ) );
+ i0.y += isYZ.x + isYZ.y;
+ i0.zw += 1.0 - isYZ.xy;
+ i0.z += isYZ.z;
+ i0.w += 1.0 - isYZ.z;
+
+ // i0 now contains the unique values 0,1,2,3 in each channel
+ vec4 i3 = clamp( i0, 0.0, 1.0 );
+ vec4 i2 = clamp( i0-1.0, 0.0, 1.0 );
+ vec4 i1 = clamp( i0-2.0, 0.0, 1.0 );
+
+ // x0 = x0 - 0.0 + 0.0 * C.xxxx
+ // x1 = x0 - i1 + 1.0 * C.xxxx
+ // x2 = x0 - i2 + 2.0 * C.xxxx
+ // x3 = x0 - i3 + 3.0 * C.xxxx
+ // x4 = x0 - 1.0 + 4.0 * C.xxxx
+ vec4 x1 = x0 - i1 + C.xxxx;
+ vec4 x2 = x0 - i2 + C.yyyy;
+ vec4 x3 = x0 - i3 + C.zzzz;
+ vec4 x4 = x0 + C.wwww;
+
+ // Permutations
+ i = _czm_mod289(i);
+ float j0 = _czm_permute( _czm_permute( _czm_permute( _czm_permute(i.w) + i.z) + i.y) + i.x);
+ vec4 j1 = _czm_permute( _czm_permute( _czm_permute( _czm_permute (
+ i.w + vec4(i1.w, i2.w, i3.w, 1.0 ))
+ + i.z + vec4(i1.z, i2.z, i3.z, 1.0 ))
+ + i.y + vec4(i1.y, i2.y, i3.y, 1.0 ))
+ + i.x + vec4(i1.x, i2.x, i3.x, 1.0 ));
+
+ // Gradients: 7x7x6 points over a cube, mapped onto a 4-cross polytope
+ // 7*7*6 = 294, which is close to the ring size 17*17 = 289.
+ vec4 ip = vec4(1.0/294.0, 1.0/49.0, 1.0/7.0, 0.0) ;
+
+ vec4 p0 = _czm_grad4(j0, ip);
+ vec4 p1 = _czm_grad4(j1.x, ip);
+ vec4 p2 = _czm_grad4(j1.y, ip);
+ vec4 p3 = _czm_grad4(j1.z, ip);
+ vec4 p4 = _czm_grad4(j1.w, ip);
+
+ // Normalise gradients
+ vec4 norm = _czm_taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
+ p0 *= norm.x;
+ p1 *= norm.y;
+ p2 *= norm.z;
+ p3 *= norm.w;
+ p4 *= _czm_taylorInvSqrt(dot(p4,p4));
+
+ // Mix contributions from the five corners
+ vec3 m0 = max(0.6 - vec3(dot(x0,x0), dot(x1,x1), dot(x2,x2)), 0.0);
+ vec2 m1 = max(0.6 - vec2(dot(x3,x3), dot(x4,x4) ), 0.0);
+ m0 = m0 * m0;
+ m1 = m1 * m1;
+ return 49.0 * ( dot(m0*m0, vec3( dot( p0, x0 ), dot( p1, x1 ), dot( p2, x2 )))
+ + dot(m1*m1, vec2( dot( p3, x3 ), dot( p4, x4 ) ) ) ) ;
+}
diff --git a/src/modules/material/shader/wall/WallDiffuseMaterial.glsl b/src/modules/material/shader/wall/WallDiffuseMaterial.glsl
new file mode 100644
index 00000000..e20dea29
--- /dev/null
+++ b/src/modules/material/shader/wall/WallDiffuseMaterial.glsl
@@ -0,0 +1,8 @@
+uniform vec4 color;
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ vec2 st = materialInput.st;
+ material.diffuse = color.rgb * 2.0;
+ material.alpha = color.a * (1.0-fract(st.t)) * 0.8;
+ return material;
+}
diff --git a/src/modules/material/shader/wall/WallImageTrailMaterial.glsl b/src/modules/material/shader/wall/WallImageTrailMaterial.glsl
new file mode 100644
index 00000000..3b73b3b6
--- /dev/null
+++ b/src/modules/material/shader/wall/WallImageTrailMaterial.glsl
@@ -0,0 +1,13 @@
+uniform sampler2D image;
+uniform vec4 color;
+uniform float speed;
+uniform vec2 repeat;
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ vec2 st = materialInput.st * repeat;
+ float time = fract(czm_frameNumber * speed / 1000.0);
+ vec4 colorImage = texture(image, vec2(fract(st.s - time), st.t));
+ material.alpha = colorImage.a * color.a ;
+ material.diffuse = colorImage.rgb * color.rgb * 3.0 ;
+ return material;
+}
diff --git a/src/modules/material/shader/wall/WallLineTrailMaterial.glsl b/src/modules/material/shader/wall/WallLineTrailMaterial.glsl
new file mode 100644
index 00000000..c8d72229
--- /dev/null
+++ b/src/modules/material/shader/wall/WallLineTrailMaterial.glsl
@@ -0,0 +1,16 @@
+uniform sampler2D image;
+uniform float speed;
+uniform vec4 color;
+uniform vec2 repeat;
+
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ float perDis = 1.0 / repeat.y / 3.0 ;
+ vec2 st = materialInput.st * repeat;
+ float time = fract(czm_frameNumber * speed / 1000.0);
+ vec4 colorImage = texture(image, vec2(st.s, fract(st.t - time)));
+ material.alpha = colorImage.a * smoothstep(.2 ,1. ,distance(st.t * perDis ,1. + perDis ));
+ material.diffuse = max(color.rgb * material.alpha * 1.5, color.rgb);
+ material.emission = max(color.rgb * material.alpha * 1.5, color.rgb);
+ return material;
+}
diff --git a/src/modules/material/shader/wall/WallRippleMaterial.glsl b/src/modules/material/shader/wall/WallRippleMaterial.glsl
new file mode 100644
index 00000000..91a99e81
--- /dev/null
+++ b/src/modules/material/shader/wall/WallRippleMaterial.glsl
@@ -0,0 +1,25 @@
+uniform vec4 color;
+uniform float speed;
+uniform float count;
+czm_material czm_getMaterial(czm_materialInput materialInput)
+{
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ vec2 st = materialInput.st;
+ float time = fract(czm_frameNumber * speed / 1000.0);
+ float sin = sin((st.t - time) * 10.0 * count);
+ float high = 0.92;
+ float medium = 0.4;
+ vec4 temp= vec4(0.);
+ if(sin > high ){
+ temp = vec4(mix(vec3(.8, 1., 1.), color.rgb, (1. - sin) / (1. - high)), 1.);
+ }else if(sin > medium) {
+ temp = vec4(color.rgb, mix(1., 0., 1.-(sin - medium) / (high - medium)));
+ }else{
+ temp = vec4(color.rgb,0);
+ }
+ vec3 fade = mix(color.rgb, vec3(0., 0., 0.), st.t);
+ temp = mix(temp, vec4(fade, 1.), 0.85);
+ material.diffuse = temp.rgb;
+ material.alpha = temp.a * (1.0 - st.t);
+ return material;
+}
diff --git a/src/modules/material/shader/wall/WallTrailMaterial.glsl b/src/modules/material/shader/wall/WallTrailMaterial.glsl
new file mode 100644
index 00000000..0cf25968
--- /dev/null
+++ b/src/modules/material/shader/wall/WallTrailMaterial.glsl
@@ -0,0 +1,18 @@
+ uniform sampler2D image;
+ uniform float speed;
+ uniform vec4 color;
+
+czm_material czm_getMaterial(czm_materialInput materialInput){
+ czm_material material = czm_getDefaultMaterial(materialInput);
+ vec2 st = materialInput.st;
+ float time = fract(czm_frameNumber * speed / 1000.0);
+ vec4 colorImage = texture(image, vec2(fract(st.t - time), st.t));
+ if(color.a == 0.0){
+ material.alpha = colorImage.a;
+ material.diffuse = colorImage.rgb;
+ }else{
+ material.alpha = colorImage.a * color.a;
+ material.diffuse = max(color.rgb * material.alpha * 3.0, color.rgb);
+ }
+ return material;
+}
diff --git a/src/modules/material/shader/weather/FogShader.glsl b/src/modules/material/shader/weather/FogShader.glsl
new file mode 100644
index 00000000..7e293025
--- /dev/null
+++ b/src/modules/material/shader/weather/FogShader.glsl
@@ -0,0 +1,37 @@
+in vec2 v_textureCoordinates;
+uniform sampler2D colorTexture;
+uniform sampler2D depthTexture;
+uniform vec4 fogByDistance;
+uniform vec4 fogColor;
+
+
+float getDistance(sampler2D depthTexture, vec2 texCoords){
+ float depth = czm_unpackDepth(texture(depthTexture, texCoords));
+ if (depth == 0.0) {
+ return czm_infinity;
+ }
+ vec4 eyeCoordinate = czm_windowToEyeCoordinates(gl_FragCoord.xy, depth);
+ return -eyeCoordinate.z / eyeCoordinate.w;
+}
+
+
+float interpolateByDistance(vec4 nearFarScalar, float distance){
+ float startDistance = nearFarScalar.x;
+ float startValue = nearFarScalar.y;
+ float endDistance = nearFarScalar.z;
+ float endValue = nearFarScalar.w;
+ float t = clamp((distance - startDistance) / (endDistance - startDistance), 0.0, 1.0);
+ return mix(startValue, endValue, t);
+}
+
+vec4 alphaBlend(vec4 sourceColor, vec4 destinationColor){
+ return sourceColor * vec4(sourceColor.aaa, 1.0) + destinationColor * (1.0 - sourceColor.a);
+}
+
+void main(void){
+ float distance = getDistance(depthTexture, v_textureCoordinates);
+ vec4 sceneColor = texture(colorTexture, v_textureCoordinates);
+ float blendAmount = interpolateByDistance(fogByDistance, distance);
+ vec4 finalFogColor = vec4(fogColor.rgb, fogColor.a * blendAmount);
+ out_FragColor = alphaBlend(finalFogColor, sceneColor);
+}
diff --git a/src/modules/material/shader/weather/RainShader.glsl b/src/modules/material/shader/weather/RainShader.glsl
new file mode 100644
index 00000000..522a59a3
--- /dev/null
+++ b/src/modules/material/shader/weather/RainShader.glsl
@@ -0,0 +1,23 @@
+in vec2 v_textureCoordinates;
+uniform sampler2D colorTexture;
+uniform float speed;
+uniform float mixNum;
+
+float hash(float x){
+ return fract(sin(x*23.3)*13.13);
+}
+
+void main(){
+ float time = czm_frameNumber * speed / 1000.0;
+ vec2 resolution = czm_viewport.zw;
+ vec2 uv=(gl_FragCoord.xy*2.-resolution.xy)/min(resolution.x,resolution.y);
+ vec3 c=vec3(.1,.2,.3);
+ float a=-.3;
+ float si=sin(a),co=cos(a);
+ uv*=mat2(co,-si,si,co);
+ uv*=length(uv+vec2(0,4.9))*.3+1.;
+ float v=1.-sin(hash(floor(uv.x*100.))*2.);
+ float b=clamp(abs(sin(20.*time*v+uv.y*(5./(2.+v))))-.95,0.,1.)*10.;
+ c*=v*b;
+ out_FragColor = mix(texture(colorTexture, v_textureCoordinates), vec4(c,1), mixNum);
+}
diff --git a/src/modules/material/shader/weather/SnowShader.glsl b/src/modules/material/shader/weather/SnowShader.glsl
new file mode 100644
index 00000000..be17629c
--- /dev/null
+++ b/src/modules/material/shader/weather/SnowShader.glsl
@@ -0,0 +1,33 @@
+in vec2 v_textureCoordinates;
+uniform sampler2D colorTexture;
+uniform float speed;
+
+float snow(vec2 uv,float scale){
+ float time = czm_frameNumber * speed / 1000.0 ;
+ float w=smoothstep(1.,0.,-uv.y*(scale/10.));
+ if(w<.1)return 0.;
+ uv+=time/scale;
+ uv.y+=time*2./scale;
+ uv.x+=sin(uv.y+time*.5)/scale;
+ uv*=scale;
+ vec2 s=floor(uv),f=fract(uv),p;
+ float k=3.,d;
+ p=.5+.35*sin(11.*fract(sin((s+p+scale)*mat2(7,3,6,5))*5.))-f;
+ d=length(p);
+ k=min(d,k);
+ k=smoothstep(0.,k,sin(f.x+f.y)*0.01);
+ return k*w;
+}
+
+void main(){
+ vec2 resolution = czm_viewport.zw;
+ vec2 uv=(gl_FragCoord.xy*2.-resolution.xy)/min(resolution.x,resolution.y);
+ vec3 finalColor=vec3(0);
+ float c = 0.0;
+ c+=snow(uv,10.);
+ c+=snow(uv,8.);
+ c+=snow(uv,6.);
+ c+=snow(uv,5.);
+ finalColor=(vec3(c));
+ out_FragColor = mix(texture(colorTexture, v_textureCoordinates), vec4(finalColor,1), 0.3);
+}
diff --git a/src/modules/material/type/circle.js b/src/modules/material/type/circle.js
new file mode 100644
index 00000000..15df6feb
--- /dev/null
+++ b/src/modules/material/type/circle.js
@@ -0,0 +1,208 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-02-27 22:49:41
+ */
+
+import { Cesium } from '../../../namespace'
+
+const CircleBlurMaterial = require('../shader/circle/CircleBlurMaterial.glsl')
+const CircleDiffuseMaterial = require('../shader/circle/CircleDiffuseMaterial.glsl')
+const CircleFadeMaterial = require('../shader/circle/CircleFadeMaterial.glsl')
+const CirclePulseMaterial = require('../shader/circle/CirclePulseMaterial.glsl')
+const CircleRingMaterial = require('../shader/circle/CircleRingMaterial.glsl')
+const CircleRotateMaterial = require('../shader/circle/CircleRotateMaterial.glsl')
+const CircleScanMaterial = require('../shader/circle/CircleScanMaterial.glsl')
+const CircleSpiralMaterial = require('../shader/circle/CircleSpiralMaterial.glsl')
+const CircleVaryMaterial = require('../shader/circle/CircleVaryMaterial.glsl')
+const CircleWaveMaterial = require('../shader/circle/CircleWaveMaterial.glsl')
+
+/**
+ * CircleBlur
+ * @type {string}
+ */
+Cesium.Material.CircleBlurType = 'CircleBlur'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.CircleBlurType, {
+ fabric: {
+ type: Cesium.Material.CircleBlurType,
+ uniforms: {
+ color: new Cesium.Color(1.0, 0.0, 0.0, 0.7),
+ speed: 3.0,
+ },
+ source: CircleBlurMaterial,
+ },
+ translucent: function (material) {
+ return true
+ },
+})
+
+/**
+ * CircleDiffuse
+ * @type {string}
+ */
+Cesium.Material.CircleDiffuseType = 'CircleDiffuse'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.CircleDiffuseType, {
+ fabric: {
+ type: Cesium.Material.CircleDiffuseType,
+ uniforms: {
+ color: new Cesium.Color(1.0, 0.0, 0.0, 0.7),
+ speed: 3.0,
+ },
+ source: CircleDiffuseMaterial,
+ },
+ translucent: function (material) {
+ return true
+ },
+})
+
+/**
+ * CircleFade
+ * @type {string}
+ */
+Cesium.Material.CircleFadeType = 'CircleFade'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.CircleFadeType, {
+ fabric: {
+ type: Cesium.Material.CircleFadeType,
+ uniforms: {
+ color: new Cesium.Color(1.0, 0.0, 0.0, 0.7),
+ speed: 3.0,
+ },
+ source: CircleFadeMaterial,
+ },
+ translucent: function (material) {
+ return true
+ },
+})
+
+/**
+ * CirclePulse
+ * @type {string}
+ */
+Cesium.Material.CirclePulseType = 'CirclePulse'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.CirclePulseType, {
+ fabric: {
+ type: Cesium.Material.CirclePulseType,
+ uniforms: {
+ color: new Cesium.Color(1.0, 0.0, 0.0, 0.7),
+ speed: 12.0,
+ },
+ source: CirclePulseMaterial,
+ },
+ translucent: function (material) {
+ return true
+ },
+})
+
+/**
+ * CircleRing
+ * @type {string}
+ */
+Cesium.Material.CircleRingType = 'CircleRing'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.CircleRingType, {
+ fabric: {
+ type: Cesium.Material.CircleRingType,
+ uniforms: {
+ color: new Cesium.Color(1.0, 0.0, 0.0, 0.7),
+ },
+ source: CircleRingMaterial,
+ },
+ translucent: function (material) {
+ return true
+ },
+})
+
+/**
+ * CircleRotate
+ * @type {string}
+ */
+Cesium.Material.CircleRotateType = 'CircleRotate'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.CircleRotateType, {
+ fabric: {
+ type: Cesium.Material.CircleRotateType,
+ uniforms: {
+ color: new Cesium.Color(1.0, 0.0, 0.0, 0.7),
+ image: Cesium.Material.DefaultImageId,
+ },
+ source: CircleRotateMaterial,
+ },
+ translucent: function (material) {
+ return true
+ },
+})
+
+/**
+ * CircleScan
+ * @type {string}
+ */
+Cesium.Material.CircleScanType = 'CircleScan'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.CircleScanType, {
+ fabric: {
+ type: Cesium.Material.CircleScanType,
+ uniforms: {
+ color: new Cesium.Color(1.0, 0.0, 0.0, 0.7),
+ speed: 1,
+ },
+ source: CircleScanMaterial,
+ },
+ translucent: function (material) {
+ return true
+ },
+})
+
+/**
+ * CircleSpiral
+ * @type {string}
+ */
+Cesium.Material.CircleSpiralType = 'CircleSpiral'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.CircleSpiralType, {
+ fabric: {
+ type: Cesium.Material.CircleSpiralType,
+ uniforms: {
+ color: new Cesium.Color(1.0, 0.0, 0.0, 0.7),
+ speed: 3.0,
+ },
+ source: CircleSpiralMaterial,
+ },
+ translucent: function (material) {
+ return true
+ },
+})
+
+/**
+ * CircleVary
+ * @type {string}
+ */
+Cesium.Material.CircleVaryType = 'CircleVary'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.CircleVaryType, {
+ fabric: {
+ type: Cesium.Material.CircleVaryType,
+ uniforms: {
+ color: new Cesium.Color(1.0, 0.0, 0.0, 0.7),
+ speed: 3.0,
+ },
+ source: CircleVaryMaterial,
+ },
+ translucent: function (material) {
+ return true
+ },
+})
+
+/**
+ * CircleWave
+ * @type {string}
+ */
+Cesium.Material.CircleWaveType = 'CircleWave'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.CircleWaveType, {
+ fabric: {
+ type: Cesium.Material.CircleWaveType,
+ uniforms: {
+ color: new Cesium.Color(1.0, 0.0, 0.0, 0.7),
+ speed: 3.0,
+ count: 1,
+ gradient: 0.1,
+ },
+ source: CircleWaveMaterial,
+ },
+ translucent: function (material) {
+ return true
+ },
+})
diff --git a/src/modules/material/type/cylinder.js b/src/modules/material/type/cylinder.js
new file mode 100644
index 00000000..c45dff29
--- /dev/null
+++ b/src/modules/material/type/cylinder.js
@@ -0,0 +1,49 @@
+/**
+ * @Author: Caven
+ * @Date: 2022-05-28 21:36:23
+ */
+
+import { Cesium } from '../../../namespace'
+
+const CylinderFadeMaterial = require('../shader/cylinder/CylinderFadeMaterial.glsl')
+const CylinderParticlesMaterial = require('../shader/cylinder/CylinderParticlesMaterial.glsl')
+
+/**
+ * CylinderFade
+ * @type {string}
+ */
+Cesium.Material.CylinderFadeType = 'CylinderFade'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.CylinderFadeType, {
+ fabric: {
+ type: Cesium.Material.CylinderFadeType,
+ uniforms: {
+ color: new Cesium.Color(1.0, 0.0, 0.0, 0.7),
+ },
+ source: CylinderFadeMaterial,
+ },
+ translucent: function (material) {
+ return true
+ },
+})
+
+/**
+ * CylinderParticles
+ * @type {string}
+ */
+Cesium.Material.CylinderParticlesType = 'CylinderParticles'
+Cesium.Material._materialCache.addMaterial(
+ Cesium.Material.CylinderParticlesType,
+ {
+ fabric: {
+ type: Cesium.Material.CylinderParticlesType,
+ uniforms: {
+ color: new Cesium.Color(1.0, 0.0, 0.0, 0.7),
+ image: Cesium.Material.DefaultImageId,
+ },
+ source: CylinderParticlesMaterial,
+ },
+ translucent: function (material) {
+ return true
+ },
+ }
+)
diff --git a/src/modules/material/type/ellipsoid.js b/src/modules/material/type/ellipsoid.js
new file mode 100644
index 00000000..759b31da
--- /dev/null
+++ b/src/modules/material/type/ellipsoid.js
@@ -0,0 +1,50 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-02-27 23:20:11
+ */
+
+import { Cesium } from '../../../namespace'
+
+const EllipsoidElectricMaterial = require('../shader/ellipsoid/EllipsoidElectricMaterial.glsl')
+const EllipsoidTrailMaterial = require('../shader/ellipsoid/EllipsoidTrailMaterial.glsl')
+
+/**
+ * EllipsoidElectric
+ * @type {string}
+ */
+Cesium.Material.EllipsoidElectricType = 'EllipsoidElectric'
+Cesium.Material._materialCache.addMaterial(
+ Cesium.Material.EllipsoidElectricType,
+ {
+ fabric: {
+ type: Cesium.Material.EllipsoidElectricType,
+ uniforms: {
+ color: new Cesium.Color(1.0, 0.0, 0.0, 0.7),
+ speed: 1,
+ },
+ source: EllipsoidElectricMaterial,
+ },
+ translucent: function (material) {
+ return true
+ },
+ }
+)
+
+/**
+ * EllipsoidTrail
+ * @type {string}
+ */
+Cesium.Material.EllipsoidTrailType = 'EllipsoidTrail'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.EllipsoidTrailType, {
+ fabric: {
+ type: Cesium.Material.EllipsoidTrailType,
+ uniforms: {
+ color: new Cesium.Color(1.0, 0.0, 0.0, 0.7),
+ speed: 3.0,
+ },
+ source: EllipsoidTrailMaterial,
+ },
+ translucent: function (material) {
+ return true
+ },
+})
diff --git a/src/modules/material/type/polyline.js b/src/modules/material/type/polyline.js
new file mode 100644
index 00000000..7ac0b62f
--- /dev/null
+++ b/src/modules/material/type/polyline.js
@@ -0,0 +1,146 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-02-27 23:23:12
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+
+const LineFlickerMaterial = require('../shader/polyline/PolylineFlickerMaterial.glsl')
+const LineFlowMaterial = require('../shader/polyline/PolylineFlowMaterial.glsl')
+const LineImageTrailMaterial = require('../shader/polyline/PolylineImageTrailMaterial.glsl')
+const LineLightingMaterial = require('../shader/polyline/PolylineLightingMaterial.glsl')
+const LineLightingTrailMaterial = require('../shader/polyline/PolylineLightingTrailMaterial.glsl')
+const LineTrailMaterial = require('../shader/polyline/PolylineTrailMaterial.glsl')
+
+/**
+ * PolylineFlicker
+ * @type {string}
+ */
+Cesium.Material.PolylineFlickerType = 'PolylineFlicker'
+Cesium.Material._materialCache.addMaterial(
+ Cesium.Material.PolylineFlickerType,
+ {
+ fabric: {
+ type: Cesium.Material.PolylineFlickerType,
+ uniforms: {
+ color: new Cesium.Color(1.0, 0.0, 0.0, 0.7),
+ speed: 1
+ },
+ source: LineFlickerMaterial
+ },
+ translucent: function(material) {
+ return true
+ }
+ }
+)
+
+/**
+ * PolylineFlow
+ * @type {string}
+ */
+Cesium.Material.PolylineFlowType = 'PolylineFlow'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.PolylineFlowType, {
+ fabric: {
+ type: Cesium.Material.PolylineFlowType,
+ uniforms: {
+ color: new Cesium.Color(1.0, 0.0, 0.0, 0.7),
+ speed: 1,
+ percent: 0.03,
+ gradient: 0.1
+ },
+ source: LineFlowMaterial
+ },
+ translucent: function(material) {
+ return true
+ }
+})
+
+/**
+ * PolylineImageTrail
+ * @type {string}
+ */
+Cesium.Material.PolylineImageTrailType = 'PolylineImageTrail'
+Cesium.Material._materialCache.addMaterial(
+ Cesium.Material.PolylineImageTrailType,
+ {
+ fabric: {
+ type: Cesium.Material.PolylineImageTrailType,
+ uniforms: {
+ color: new Cesium.Color(1.0, 0.0, 0.0, 0.7),
+ image: Cesium.Material.DefaultImageId,
+ speed: 1,
+ repeat: new Cesium.Cartesian2(1, 1)
+ },
+ source: LineImageTrailMaterial
+ },
+ translucent: function(material) {
+ return true
+ }
+ }
+)
+
+/**
+ * PolylineLighting
+ * @type {string}
+ */
+Cesium.Material.PolylineLightingType = 'PolylineLighting'
+Cesium.Material._materialCache.addMaterial(
+ Cesium.Material.PolylineLightingType,
+ {
+ fabric: {
+ type: Cesium.Material.PolylineLightingType,
+ uniforms: {
+ color: new Cesium.Color(1.0, 0.0, 0.0, 0.7),
+ image: Cesium.Material.DefaultImageId
+ },
+ source: LineLightingMaterial
+ },
+ translucent: function(material) {
+ return true
+ }
+ }
+)
+
+/**
+ * PolylineLightingTrail
+ * @type {string}
+ */
+Cesium.Material.PolylineLightingTrailType = 'PolylineLightingTrail'
+Cesium.Material._materialCache.addMaterial(
+ Cesium.Material.PolylineLightingTrailType,
+ {
+ fabric: {
+ type: Cesium.Material.PolylineLightingTrailType,
+ uniforms: {
+ color: new Cesium.Color(1.0, 0.0, 0.0, 0.7),
+ image: Cesium.Material.DefaultImageId,
+ speed: 3.0
+ },
+ source: LineLightingTrailMaterial
+ },
+ translucent: function(material) {
+ return true
+ }
+ }
+)
+
+/**
+ * PolylineTrail
+ * @type {string}
+ */
+Cesium.Material.PolylineTrailType = 'PolylineTrail'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.PolylineTrailType, {
+ fabric: {
+ type: Cesium.Material.PolylineTrailType,
+ uniforms: {
+ color: new Cesium.Color(1.0, 0.0, 0.0, 0.7),
+ image: Cesium.Material.DefaultImageId,
+ speed: 1,
+ repeat: new Cesium.Cartesian2(1, 1)
+ },
+ source: LineTrailMaterial
+ },
+ translucent: function(material) {
+ return true
+ }
+})
diff --git a/src/modules/material/type/radar.js b/src/modules/material/type/radar.js
new file mode 100644
index 00000000..46382f0d
--- /dev/null
+++ b/src/modules/material/type/radar.js
@@ -0,0 +1,67 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-02-27 23:22:38
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+
+const RadarLineMaterial = require('../shader/radar/RadarLineMaterial.glsl')
+const RadarSweepMaterial = require('../shader/radar/RadarSweepMaterial.glsl')
+const RadarWaveMaterial = require('../shader/radar/RadarWaveMaterial.glsl')
+
+/**
+ * RadarLine
+ * @type {string}
+ */
+Cesium.Material.RadarLineType = 'RadarLine'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.RadarLineType, {
+ fabric: {
+ type: Cesium.Material.RadarLineType,
+ uniforms: {
+ color: new Cesium.Color(1.0, 0.0, 0.0, 0.7),
+ speed: 3.0
+ },
+ source: RadarLineMaterial
+ },
+ translucent: function(material) {
+ return true
+ }
+})
+
+/**
+ * RadarSweep
+ * @type {string}
+ */
+Cesium.Material.RadarSweepType = 'RadarSweep'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.RadarSweepType, {
+ fabric: {
+ type: Cesium.Material.RadarSweepType,
+ uniforms: {
+ color: new Cesium.Color(1.0, 0.0, 0.0, 0.7),
+ speed: 3.0
+ },
+ source: RadarSweepMaterial
+ },
+ translucent: function(material) {
+ return true
+ }
+})
+
+/**
+ * RadarWave
+ * @type {string}
+ */
+Cesium.Material.RadarWaveType = 'RadarWave'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.RadarWaveType, {
+ fabric: {
+ type: Cesium.Material.RadarWaveType,
+ uniforms: {
+ color: new Cesium.Color(1.0, 0.0, 0.0, 0.7),
+ speed: 3.0
+ },
+ source: RadarWaveMaterial
+ },
+ translucent: function(material) {
+ return true
+ }
+})
diff --git a/src/modules/material/type/thirdpart.js b/src/modules/material/type/thirdpart.js
new file mode 100644
index 00000000..559dfc49
--- /dev/null
+++ b/src/modules/material/type/thirdpart.js
@@ -0,0 +1,272 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-02-27 22:52:23
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+
+const czm_cellular = require('../shader/thirdpart/cellular.glsl')
+const czm_snoise = require('../shader/thirdpart/snoise.glsl')
+const AsphaltMaterial = require('../shader/thirdpart/AsphaltMaterial.glsl')
+const BlobMaterial = require('../shader/thirdpart/BlobMaterial.glsl')
+const BrickMaterial = require('../shader/thirdpart/BlobMaterial.glsl')
+const CementMaterial = require('../shader/thirdpart/CementMaterial.glsl')
+const ErosionMaterial = require('../shader/thirdpart/ErosionMaterial.glsl')
+const FacetMaterial = require('../shader/thirdpart/FacetMaterial.glsl')
+const FresnelMaterial = require('../shader/thirdpart/FresnelMaterial.glsl')
+const GrassMaterial = require('../shader/thirdpart/GrassMaterial.glsl')
+const ReflectionMaterial = require('../shader/thirdpart/ReflectionMaterial.glsl')
+const RefractionMaterial = require('../shader/thirdpart/RefractionMaterial.glsl')
+const TieDyeMaterial = require('../shader/thirdpart/TieDyeMaterial.glsl')
+const WoodMaterial = require('../shader/thirdpart/WoodMaterial.glsl')
+
+Cesium.ShaderSource._czmBuiltinsAndUniforms.czm_cellular = czm_cellular
+Cesium.ShaderSource._czmBuiltinsAndUniforms.czm_snoise = czm_snoise
+
+/**
+ * Asphalt
+ * @type {string}
+ */
+Cesium.Material.AsphaltType = 'Asphalt'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.AsphaltType, {
+ fabric: {
+ type: Cesium.Material.AsphaltType,
+ uniforms: {
+ asphaltColor: new Cesium.Color(0.15, 0.15, 0.15, 1.0),
+ bumpSize: 0.02,
+ roughness: 0.2
+ },
+ source: AsphaltMaterial
+ },
+ translucent: function(material) {
+ return material.uniforms.asphaltColor.alpha < 1.0
+ }
+})
+
+/**
+ * Blob
+ * @type {string}
+ */
+Cesium.Material.BlobType = 'Blob'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.BlobType, {
+ fabric: {
+ type: Cesium.Material.BlobType,
+ uniforms: {
+ lightColor: new Cesium.Color(1.0, 1.0, 1.0, 0.5),
+ darkColor: new Cesium.Color(0.0, 0.0, 1.0, 0.5),
+ frequency: 10.0
+ },
+ source: BlobMaterial
+ },
+ translucent: function(material) {
+ var uniforms = material.uniforms
+ return uniforms.lightColor.alpha < 1.0 || uniforms.darkColor.alpha < 0.0
+ }
+})
+
+/**
+ * Brick
+ * @type {string}
+ */
+Cesium.Material.BrickType = 'Brick'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.BrickType, {
+ fabric: {
+ type: Cesium.Material.BrickType,
+ uniforms: {
+ brickColor: new Cesium.Color(0.6, 0.3, 0.1, 1.0),
+ mortarColor: new Cesium.Color(0.8, 0.8, 0.7, 1.0),
+ brickSize: new Cesium.Cartesian2(0.3, 0.15),
+ brickPct: new Cesium.Cartesian2(0.9, 0.85),
+ brickRoughness: 0.2,
+ mortarRoughness: 0.1
+ },
+ source: BrickMaterial
+ },
+ translucent: function(material) {
+ var uniforms = material.uniforms
+ return uniforms.brickColor.alpha < 1.0 || uniforms.mortarColor.alpha < 1.0
+ }
+})
+
+/**
+ * Cement
+ * @type {string}
+ */
+Cesium.Material.CementType = 'Cement'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.CementType, {
+ fabric: {
+ type: Cesium.Material.CementType,
+ uniforms: {
+ cementColor: new Cesium.Color(0.95, 0.95, 0.85, 1.0),
+ grainScale: 0.01,
+ roughness: 0.3
+ },
+ source: CementMaterial
+ },
+ translucent: function(material) {
+ return material.uniforms.cementColor.alpha < 1.0
+ }
+})
+
+/**
+ * Erosion
+ * @type {string}
+ */
+Cesium.Material.ErosionType = 'Erosion'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.ErosionType, {
+ fabric: {
+ type: Cesium.Material.ErosionType,
+ uniforms: {
+ color: new Cesium.Color(1.0, 0.0, 0.0, 0.5),
+ time: 1.0
+ },
+ source: ErosionMaterial
+ },
+ translucent: function(material) {
+ return material.uniforms.color.alpha < 1.0
+ }
+})
+
+/**
+ * Facet
+ * @type {string}
+ */
+Cesium.Material.FacetType = 'Facet'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.FacetType, {
+ fabric: {
+ type: Cesium.Material.FacetType,
+ uniforms: {
+ lightColor: new Cesium.Color(0.25, 0.25, 0.25, 0.75),
+ darkColor: new Cesium.Color(0.75, 0.75, 0.75, 0.75),
+ frequency: 10.0
+ },
+ source: FacetMaterial
+ },
+ translucent: function(material) {
+ var uniforms = material.uniforms
+ return uniforms.lightColor.alpha < 1.0 || uniforms.darkColor.alpha < 0.0
+ }
+})
+
+/**
+ * Fresnel
+ * @type {string}
+ */
+Cesium.Material.FresnelType = 'Fresnel'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.FresnelType, {
+ fabric: {
+ type: Cesium.Material.FresnelType,
+ materials: {
+ reflection: {
+ type: Cesium.Material.ReflectionType
+ },
+ refraction: {
+ type: Cesium.Material.RefractionType
+ }
+ },
+ source: FresnelMaterial
+ },
+ translucent: false
+})
+
+/**
+ * Grass
+ * @type {string}
+ */
+Cesium.Material.GrassType = 'Grass'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.GrassType, {
+ fabric: {
+ type: Cesium.Material.GrassType,
+ uniforms: {
+ grassColor: new Cesium.Color(0.25, 0.4, 0.1, 1.0),
+ dirtColor: new Cesium.Color(0.1, 0.1, 0.1, 1.0),
+ patchiness: 1.5
+ },
+ source: GrassMaterial
+ },
+ translucent: function(material) {
+ var uniforms = material.uniforms
+ return uniforms.grassColor.alpha < 1.0 || uniforms.dirtColor.alpha < 1.0
+ }
+})
+
+/**
+ * Reflection
+ * @type {string}
+ */
+Cesium.Material.ReflectionType = 'Reflection'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.ReflectionType, {
+ fabric: {
+ type: Cesium.Material.ReflectionType,
+ uniforms: {
+ cubeMap: Cesium.Material.DefaultCubeMapId,
+ channels: 'rgb'
+ },
+ source: ReflectionMaterial
+ },
+ translucent: false
+})
+
+/**
+ * Refraction
+ * @type {string}
+ */
+Cesium.Material.RefractionType = 'Refraction'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.RefractionType, {
+ fabric: {
+ type: Cesium.Material.RefractionType,
+ uniforms: {
+ cubeMap: Cesium.Material.DefaultCubeMapId,
+ channels: 'rgb',
+ indexOfRefractionRatio: 0.9
+ },
+ source: RefractionMaterial
+ },
+ translucent: false
+})
+
+/**
+ * TieDye
+ * @type {string}
+ */
+Cesium.Material.TyeDyeType = 'TieDye'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.TyeDyeType, {
+ fabric: {
+ type: Cesium.Material.TyeDyeType,
+ uniforms: {
+ lightColor: new Cesium.Color(1.0, 1.0, 0.0, 0.75),
+ darkColor: new Cesium.Color(1.0, 0.0, 0.0, 0.75),
+ frequency: 5.0
+ },
+ source: TieDyeMaterial
+ },
+ translucent: function(material) {
+ var uniforms = material.uniforms
+ return uniforms.lightColor.alpha < 1.0 || uniforms.darkColor.alpha < 0.0
+ }
+})
+
+/**
+ * Wood
+ * @type {string}
+ */
+Cesium.Material.WoodType = 'Wood'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.WoodType, {
+ fabric: {
+ type: Cesium.Material.WoodType,
+ uniforms: {
+ lightWoodColor: new Cesium.Color(0.6, 0.3, 0.1, 1.0),
+ darkWoodColor: new Cesium.Color(0.4, 0.2, 0.07, 1.0),
+ ringFrequency: 3.0,
+ noiseScale: new Cesium.Cartesian2(0.7, 0.5),
+ grainFrequency: 27.0
+ },
+ source: WoodMaterial
+ },
+ translucent: function(material) {
+ let uniforms = material.uniforms
+ return (
+ uniforms.lightWoodColor.alpha < 1.0 || uniforms.darkWoodColor.alpha < 1.0
+ )
+ }
+})
diff --git a/src/modules/material/type/wall.js b/src/modules/material/type/wall.js
new file mode 100644
index 00000000..e35b0119
--- /dev/null
+++ b/src/modules/material/type/wall.js
@@ -0,0 +1,91 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-02-27 23:30:43
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+
+const WallDiffuseMaterial = require('../shader/wall/WallDiffuseMaterial.glsl')
+const WallImageTrailMaterial = require('../shader/wall/WallImageTrailMaterial.glsl')
+const WallLineTrailMaterial = require('../shader/wall/WallLineTrailMaterial.glsl')
+const WallTrailMaterial = require('../shader/wall/WallTrailMaterial.glsl')
+
+/**
+ * WallDiffuse
+ * @type {string}
+ */
+Cesium.Material.WallDiffuseType = 'WallDiffuse'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.WallDiffuseType, {
+ fabric: {
+ type: Cesium.Material.WallDiffuseType,
+ uniforms: {
+ color: new Cesium.Color(1.0, 0.0, 0.0, 0.7)
+ },
+ source: WallDiffuseMaterial
+ },
+ translucent: function(material) {
+ return true
+ }
+})
+
+/**
+ * WallImageTrail
+ * @type {string}
+ */
+Cesium.Material.WallImageTrailType = 'WallImageTrail'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.WallImageTrailType, {
+ fabric: {
+ type: Cesium.Material.WallImageTrailType,
+ uniforms: {
+ image: Cesium.Material.DefaultImageId,
+ color: new Cesium.Color(1.0, 0.0, 0.0, 0.7),
+ speed: 3.0,
+ repeat: new Cesium.Cartesian2(1, 1)
+ },
+ source: WallImageTrailMaterial
+ },
+ translucent: function(material) {
+ return true
+ }
+})
+
+/**
+ * WallLineTrail
+ * @type {string}
+ */
+Cesium.Material.WallLineTrailType = 'WallLineTrail'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.WallLineTrailType, {
+ fabric: {
+ type: Cesium.Material.WallLineTrailType,
+ uniforms: {
+ color: new Cesium.Color(1.0, 0.0, 0.0, 0.7),
+ image: Cesium.Material.DefaultImageId,
+ repeat: new Cesium.Cartesian2(1, 1),
+ speed: 3.0
+ },
+ source: WallLineTrailMaterial
+ },
+ translucent: function(material) {
+ return true
+ }
+})
+
+/**
+ * WallTrail
+ * @type {string}
+ */
+Cesium.Material.WallTrailType = 'WallTrail'
+Cesium.Material._materialCache.addMaterial(Cesium.Material.WallTrailType, {
+ fabric: {
+ type: Cesium.Material.WallTrailType,
+ uniforms: {
+ color: new Cesium.Color(1.0, 0.0, 0.0, 0.7),
+ image: Cesium.Material.DefaultImageId,
+ speed: 1
+ },
+ source: WallTrailMaterial
+ },
+ translucent: function(material) {
+ return true
+ }
+})
diff --git a/src/modules/math/area.js b/src/modules/math/area.js
new file mode 100644
index 00000000..a9dbae87
--- /dev/null
+++ b/src/modules/math/area.js
@@ -0,0 +1,62 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-04-24 14:49:37
+ */
+
+import { Cesium } from '../../namespace'
+import { Transform } from '../transform'
+
+/**
+ *
+ * @param p0
+ * @param p1
+ * @param p2
+ * @returns {number}
+ * @private
+ */
+function triangleArea(p0, p1, p2) {
+ let v0 = Cesium.Cartesian3.subtract(p0, p1, new Cesium.Cartesian3())
+ let v1 = Cesium.Cartesian3.subtract(p2, p1, new Cesium.Cartesian3())
+ let cross = Cesium.Cartesian3.cross(v0, v1, v0)
+ return Cesium.Cartesian3.magnitude(cross) * 0.5
+}
+
+export default function area(positions) {
+ let result = 0
+ if (!Array.isArray(positions)) {
+ return result
+ }
+ if (!(positions[0] instanceof Cesium.Cartesian3)) {
+ positions = Transform.transformWGS84ArrayToCartesianArray(positions)
+ }
+ let geometry = Cesium.CoplanarPolygonGeometry.createGeometry(
+ Cesium.CoplanarPolygonGeometry.fromPositions({
+ positions: positions,
+ vertexFormat: Cesium.VertexFormat.POSITION_ONLY,
+ })
+ )
+ if (!geometry) {
+ return result
+ }
+ let flatPositions = geometry.attributes.position.values
+ let indices = geometry.indices
+ for (let i = 0; i < indices.length; i += 3) {
+ let p0 = Cesium.Cartesian3.unpack(
+ flatPositions,
+ indices[i] * 3,
+ new Cesium.Cartesian3()
+ )
+ let p1 = Cesium.Cartesian3.unpack(
+ flatPositions,
+ indices[i + 1] * 3,
+ new Cesium.Cartesian3()
+ )
+ let p2 = Cesium.Cartesian3.unpack(
+ flatPositions,
+ indices[i + 2] * 3,
+ new Cesium.Cartesian3()
+ )
+ result += triangleArea(p0, p1, p2)
+ }
+ return result
+}
diff --git a/src/modules/math/bounds.js b/src/modules/math/bounds.js
new file mode 100644
index 00000000..e6fe9978
--- /dev/null
+++ b/src/modules/math/bounds.js
@@ -0,0 +1,32 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-04-23 09:29:56
+ */
+
+export default function bounds(positions = [], expand = 0) {
+ let minLng = 180
+ let minLat = 90
+ let maxLng = -180
+ let maxLat = -90
+ positions.forEach((item) => {
+ minLng = Math.min(minLng, item.lng || item.x)
+ minLat = Math.min(minLat, item.lat || item.y)
+ maxLng = Math.max(maxLng, item.lng || item.x)
+ maxLat = Math.max(maxLat, item.lat || item.y)
+ })
+
+ if (expand > 0) {
+ let diffLng = Math.abs(maxLng - maxLng)
+ let diffLat = Math.abs(maxLat - minLat)
+ minLng -= diffLng * expand
+ minLat -= diffLat * expand
+ maxLng += diffLng * expand
+ maxLat += diffLat * expand
+ }
+ return {
+ west: minLng,
+ south: minLat,
+ east: maxLng,
+ north: maxLat,
+ }
+}
diff --git a/src/modules/math/center.js b/src/modules/math/center.js
new file mode 100644
index 00000000..3707d40b
--- /dev/null
+++ b/src/modules/math/center.js
@@ -0,0 +1,19 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-04-24 14:43:39
+ */
+
+import { Cesium } from '../../namespace'
+import { Transform } from '../transform'
+import Position from '../position/Position'
+
+export default function center(positions) {
+ if (positions && Array.isArray(positions)) {
+ let boundingSphere = Cesium.BoundingSphere.fromPoints(
+ Transform.transformWGS84ArrayToCartesianArray(positions)
+ )
+ return Transform.transformCartesianToWGS84(boundingSphere.center)
+ }
+
+ return new Position()
+}
diff --git a/src/modules/math/curve.js b/src/modules/math/curve.js
new file mode 100644
index 00000000..15101fc3
--- /dev/null
+++ b/src/modules/math/curve.js
@@ -0,0 +1,92 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-16 11:14:23
+ */
+
+/**
+ * Some of the code borrows from MAPV
+ * https://github.com/huiyan-fe/mapv/blob/3292c7c25dbbf29af3cf7b3acb48108d60b3eed8/src/utils/curve.js
+ */
+export default function curve(points, options) {
+ options = options || {}
+ let curvePoints = []
+ for (let i = 0; i < points.length - 1; i++) {
+ let p = getCurveByTwoPoints(points[i], points[i + 1], options.count)
+ if (p && p.length > 0) {
+ curvePoints = curvePoints.concat(p)
+ }
+ }
+ return curvePoints
+}
+
+/**
+ * Get a curvilinear coordinate set of points based on two points
+ * @param obj1
+ * @param obj2
+ * @param count
+ * @returns {null|[]}
+ */
+function getCurveByTwoPoints(obj1, obj2, count) {
+ if (!obj1 || !obj2) {
+ return null
+ }
+ let curveCoordinates = []
+ count = count || 40 // 曲线是由一些小的线段组成的,这个表示这个曲线所有到的折线的个数
+ let B1 = function(x) {
+ return 1 - 2 * x + x * x
+ }
+ let B2 = x => {
+ return 2 * x - 2 * x * x
+ }
+ let B3 = x => {
+ return x * x
+ }
+
+ let t, h, h2, lat3, lng3, t2
+ let inc = 0
+ let lat1 = parseFloat(obj1.lat)
+ let lat2 = parseFloat(obj2.lat)
+ let lng1 = parseFloat(obj1.lng)
+ let lng2 = parseFloat(obj2.lng)
+
+ // 计算曲线角度的方法
+ if (lng2 > lng1) {
+ if (lng2 - lng1 > 180) {
+ if (lng1 < 0) {
+ lng1 = 180 + 180 + lng1
+ lng2 = 180 + 180 + lng2
+ }
+ }
+ }
+ // 此时纠正了 lng1 lng2
+
+ t2 = 0
+ // 纬度相同
+ if (lat2 === lat1) {
+ t = 0
+ h = lng1 - lng2
+ // 经度相同
+ } else if (lng2 === lng1) {
+ t = Math.PI / 2
+ h = lat1 - lat2
+ } else {
+ t = Math.atan((lat2 - lat1) / (lng2 - lng1))
+ h = (lat2 - lat1) / Math.sin(t)
+ }
+ if (t2 === 0) {
+ t2 = t + Math.PI / 5
+ }
+ h2 = h / 2
+ lng3 = h2 * Math.cos(t2) + lng1
+ lat3 = h2 * Math.sin(t2) + lat1
+
+ for (let i = 0; i < count + 1; i++) {
+ let x = lng1 * B1(inc) + lng3 * B2(inc) + lng2 * B3(inc)
+ let y = lat1 * B1(inc) + lat3 * B2(inc) + lat2 * B3(inc)
+ let lng1_src = obj1.lng
+ let lng2_src = obj2.lng
+ curveCoordinates.push([lng1_src < 0 && lng2_src > 0 ? x - 360 : x, y])
+ inc = inc + 1 / count
+ }
+ return curveCoordinates
+}
diff --git a/src/modules/math/distance.js b/src/modules/math/distance.js
new file mode 100644
index 00000000..d676e00f
--- /dev/null
+++ b/src/modules/math/distance.js
@@ -0,0 +1,24 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-03-31 20:58:06
+ */
+
+import { Cesium } from '../../namespace'
+import { Transform } from '../transform'
+
+export default function distance(positions) {
+ let distance = 0
+ if (positions && Array.isArray(positions)) {
+ for (let i = 0; i < positions.length - 1; i++) {
+ let c1 = Transform.transformWGS84ToCartographic(positions[i])
+ let c2 = Transform.transformWGS84ToCartographic(positions[i + 1])
+ let geodesic = new Cesium.EllipsoidGeodesic()
+ geodesic.setEndPoints(c1, c2)
+ let s = geodesic.surfaceDistance
+ s = Math.sqrt(Math.pow(s, 2) + Math.pow(c2.height - c1.height, 2))
+ distance += s
+ }
+ }
+
+ return distance.toFixed(3)
+}
diff --git a/src/modules/math/heading.js b/src/modules/math/heading.js
new file mode 100644
index 00000000..37f4fc05
--- /dev/null
+++ b/src/modules/math/heading.js
@@ -0,0 +1,44 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-04-11 00:41:47
+ */
+
+import { Cesium } from '../../namespace'
+import { Transform } from '../transform'
+import Parse from '../parse/Parse'
+
+export default function heading(start, end) {
+ let startPosition = start
+ let endPosition = end
+ if (!startPosition || !endPosition) {
+ return 0
+ }
+
+ if (!(start instanceof Cesium.Cartesian3)) {
+ startPosition = Parse.parsePosition(start)
+ startPosition = Transform.transformWGS84ToCartesian(startPosition)
+ }
+
+ if (!(end instanceof Cesium.Cartesian3)) {
+ endPosition = Parse.parsePosition(end)
+ endPosition = Transform.transformWGS84ToCartesian(endPosition)
+ }
+
+ let ff = Cesium.Transforms.eastNorthUpToFixedFrame(startPosition)
+ let v = Cesium.Cartesian3.subtract(
+ endPosition,
+ startPosition,
+ new Cesium.Cartesian3()
+ )
+ let vector = Cesium.Matrix4.multiplyByPointAsVector(
+ Cesium.Matrix4.inverse(ff, new Cesium.Matrix4()),
+ v,
+ new Cesium.Cartesian3()
+ )
+
+ let heading = Math.atan2(vector.y, vector.x) - Cesium.Math.PI_OVER_TWO
+
+ heading = Cesium.Math.TWO_PI - Cesium.Math.zeroToTwoPi(heading)
+
+ return isNaN(heading) ? 0 : heading
+}
diff --git a/src/modules/math/index.js b/src/modules/math/index.js
new file mode 100644
index 00000000..3f6832e0
--- /dev/null
+++ b/src/modules/math/index.js
@@ -0,0 +1,15 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-03-31 20:57:36
+ */
+
+export { default as area } from './area'
+export { default as bounds } from './bounds'
+export { default as center } from './center'
+export { default as curve } from './curve'
+export { default as distance } from './distance'
+export { default as heading } from './heading'
+export { default as isBetween } from './isBetween'
+export { default as midCartesian } from './midCartesian'
+export { default as midPosition } from './midPosition'
+export { default as parabola } from './parabola'
diff --git a/src/modules/math/isBetween.js b/src/modules/math/isBetween.js
new file mode 100644
index 00000000..e97f47e6
--- /dev/null
+++ b/src/modules/math/isBetween.js
@@ -0,0 +1,9 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-03-31 20:58:06
+ */
+
+export default function isBetween(value, min, max) {
+ value = parseFloat(value) || 0.0
+ return value >= parseFloat(min) && value <= parseFloat(max)
+}
diff --git a/src/modules/math/midCartesian.js b/src/modules/math/midCartesian.js
new file mode 100644
index 00000000..ba934034
--- /dev/null
+++ b/src/modules/math/midCartesian.js
@@ -0,0 +1,13 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-21 18:16:52
+ */
+
+import { Cesium } from '../../namespace'
+
+export default function midCartesian(start, end) {
+ let c1 = Cesium.Ellipsoid.WGS84.cartesianToCartographic(start)
+ let c2 = Cesium.Ellipsoid.WGS84.cartesianToCartographic(end)
+ let cm = new Cesium.EllipsoidGeodesic(c1, c2).interpolateUsingFraction(0.5)
+ return Cesium.Ellipsoid.WGS84.cartographicToCartesian(cm)
+}
diff --git a/src/modules/math/midPosition.js b/src/modules/math/midPosition.js
new file mode 100644
index 00000000..b703e8b3
--- /dev/null
+++ b/src/modules/math/midPosition.js
@@ -0,0 +1,26 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-21 18:16:52
+ */
+
+import { Cesium } from '../../namespace'
+import { Transform } from '../transform'
+import Parse from '../parse/Parse'
+import Position from '../position/Position'
+
+export default function midPosition(start, end) {
+ let startPosition = Parse.parsePosition(start)
+ let endPosition = Parse.parsePosition(end)
+ startPosition = Transform.transformWGS84ToCartographic(startPosition)
+ endPosition = Transform.transformWGS84ToCartographic(endPosition)
+ let mc = new Cesium.EllipsoidGeodesic(
+ startPosition,
+ endPosition
+ ).interpolateUsingFraction(0.5)
+
+ return new Position(
+ Cesium.Math.toDegrees(mc.longitude),
+ Cesium.Math.toDegrees(mc.latitude),
+ mc.height
+ )
+}
diff --git a/src/modules/math/parabola.js b/src/modules/math/parabola.js
new file mode 100644
index 00000000..5e1b02ba
--- /dev/null
+++ b/src/modules/math/parabola.js
@@ -0,0 +1,53 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-05-28 10:24:38
+ */
+
+export default function parabola(
+ startPosition,
+ endPosition,
+ height = 0,
+ count = 50
+) {
+ //方程 y=-(4h/L^2)*x^2+h h:顶点高度 L:横纵间距较大者
+ let result = []
+ height = Math.max(+height, 100)
+ count = Math.max(+count, 50)
+ let diffLng = Math.abs(startPosition.lng - endPosition.lng)
+ let diffLat = Math.abs(startPosition.lat - endPosition.lat)
+ let L = Math.max(diffLng, diffLat)
+ let dlt = L / count
+ if (diffLng > diffLat) {
+ //base on lng
+ let delLat = (endPosition.lat - startPosition.lat) / count
+ if (startPosition.lng - endPosition.lng > 0) {
+ dlt = -dlt
+ }
+ for (let i = 0; i < count; i++) {
+ let h =
+ height -
+ (Math.pow(-0.5 * L + Math.abs(dlt) * i, 2) * 4 * height) /
+ Math.pow(L, 2)
+ let lng = startPosition.lng + dlt * i
+ let lat = startPosition.lat + delLat * i
+ result.push([lng, lat, h])
+ }
+ } else {
+ //base on lat
+ let delLng = (endPosition.lng - startPosition.lng) / count
+ if (startPosition.lat - endPosition.lat > 0) {
+ dlt = -dlt
+ }
+ for (let i = 0; i < count; i++) {
+ let h =
+ height -
+ (Math.pow(-0.5 * L + Math.abs(dlt) * i, 2) * 4 * height) /
+ Math.pow(L, 2)
+ let lng = startPosition.lng + delLng * i
+ let lat = startPosition.lat + dlt * i
+ result.push([lng, lat, h])
+ }
+ }
+ result.push([endPosition.lng, endPosition.lat, endPosition.alt || 0])
+ return result
+}
diff --git a/src/modules/measure/Measure.js b/src/modules/measure/Measure.js
new file mode 100644
index 00000000..9c730696
--- /dev/null
+++ b/src/modules/measure/Measure.js
@@ -0,0 +1,189 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-07-11 09:56:33
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+import Angle from './type/Angle'
+import Area from './type/Area'
+import AreaHeight from './type/AreaHeight'
+import AreaSurface from './type/AreaSurface'
+import Distance from './type/Distance'
+import DistanceSurface from './type/DistanceSurface'
+import Heading from './type/Heading'
+import Height from './type/Height'
+import TriangleHeight from './type/TriangleHeight'
+import MeasureType from './MeasureType'
+
+class Measure {
+ constructor() {
+ this._viewer = undefined
+ this._layer = new Cesium.CustomDataSource('measure-layer')
+ }
+
+ get viewer() {
+ return this._viewer
+ }
+
+ get layer() {
+ return this._layer
+ }
+
+ /**
+ *
+ * @param options
+ * @returns {Measure}
+ */
+ angle(options = {}) {
+ new Angle().start(this, options)
+ return this
+ }
+
+ /**
+ *
+ * @param options
+ * @returns {Measure}
+ */
+ area(options = {}) {
+ new Area().start(this, options)
+ return this
+ }
+
+ /**
+ *
+ * @param options
+ * @returns {Measure}
+ */
+ areaHeight(options = {}) {
+ new AreaHeight().start(this, options)
+ return this
+ }
+
+ /**
+ *
+ * @param options
+ * @returns {Measure}
+ */
+ areaSurface(options = {}) {
+ new AreaSurface().start(this, options)
+ return this
+ }
+
+ /**
+ *
+ * @param options
+ * @returns {Measure}
+ */
+ distance(options = {}) {
+ new Distance().start(this, options)
+ return this
+ }
+
+ /**
+ *
+ * @param options
+ * @returns {Measure}
+ */
+ distanceSurface(options = {}) {
+ new DistanceSurface().start(this, options)
+ return this
+ }
+
+ /**
+ *
+ * @param options
+ * @returns {Measure}
+ */
+ heading(options = {}) {
+ new Heading().start(this, options)
+ return this
+ }
+
+ /**
+ *
+ * @param options
+ * @returns {Measure}
+ */
+ height(options = {}) {
+ new Height().start(this, options)
+ return this
+ }
+
+ /**
+ *
+ * @param options
+ * @returns {Measure}
+ */
+ triangleHeight(options = {}) {
+ new TriangleHeight().start(this, options)
+ return this
+ }
+ /**
+ *
+ * @param viewer
+ */
+ install(viewer) {
+ this._viewer = viewer
+ this._viewer.dataSources.add(this._layer)
+ Object.defineProperty(viewer, 'measure', {
+ value: this,
+ writable: false
+ })
+ }
+
+ /**
+ *
+ * @param type
+ * @param options
+ * @returns {Measure}
+ */
+ activate(type, options) {
+ switch (type) {
+ case MeasureType.ANGLE:
+ this.angle(options)
+ break
+ case MeasureType.AREA:
+ this.area(options)
+ break
+ case MeasureType.AREA_HEIGHT:
+ this.areaHeight(options)
+ break
+ case MeasureType.AREA_SURFACE:
+ this.areaSurface(options)
+ break
+ case MeasureType.DISTANCE:
+ this.distance(options)
+ break
+ case MeasureType.DISTANCE_SURFACE:
+ this.distanceSurface(options)
+ break
+ case MeasureType.HEADING:
+ this.heading(options)
+ break
+ case MeasureType.HEIGHT:
+ this.height(options)
+ break
+ case MeasureType.TRIANGLE_HEIGHT:
+ this.triangleHeight(options)
+ break
+ default:
+ break
+ }
+ return this
+ }
+
+ /**
+ *
+ * @returns {Measure}
+ */
+ deactivate() {
+ this._layer.entities.removeAll()
+ this._viewer.drawTool.tooltipMess = ''
+ this._viewer.drawTool.deactivate()
+ this._viewer.editTool.tooltipMess = ''
+ this._viewer.editTool.deactivate()
+ return this
+ }
+}
+
+export default Measure
diff --git a/src/modules/measure/MeasureBase.js b/src/modules/measure/MeasureBase.js
new file mode 100644
index 00000000..a0ade991
--- /dev/null
+++ b/src/modules/measure/MeasureBase.js
@@ -0,0 +1,100 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-07-11 09:56:33
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+
+class MeasureBase {
+ constructor() {
+ this._viewer = undefined
+ this._layer = undefined
+ this._startLabel = new Cesium.Entity({
+ label: {
+ text: '开始',
+ font: '12px',
+ pixelOffset: { x: 0, y: -15 },
+ disableDepthTestDistance: Number.POSITIVE_INFINITY,
+ showBackground: true
+ }
+ })
+ this._resultLabel = new Cesium.Entity({
+ label: {
+ font: '12px',
+ pixelOffset: { x: 0, y: -15 },
+ disableDepthTestDistance: Number.POSITIVE_INFINITY,
+ showBackground: true
+ }
+ })
+ this._options = {}
+ }
+
+ /**
+ *
+ * @param positions
+ * @param includeModel
+ * @returns {*}
+ */
+ _getSampledHeight(positions, includeModel = false) {
+ let terrainPromise =
+ this._viewer.terrainProvider &&
+ !(this._viewer.terrainProvider instanceof Cesium.EllipsoidTerrainProvider)
+ ? Cesium.sampleTerrainMostDetailed(
+ this._viewer.terrainProvider,
+ positions.map(item => Cesium.Cartographic.fromCartesian(item))
+ )
+ : Promise.resolve(
+ positions.map(item => Cesium.Cartographic.fromCartesian(item))
+ )
+
+ let modelPromise =
+ this._viewer.scene.clampToHeightSupported && includeModel
+ ? this._viewer.scene.clampToHeightMostDetailed(
+ positions,
+ this._layer.entities.values
+ )
+ : Promise.resolve(positions)
+
+ return Promise.all([terrainPromise, modelPromise])
+ }
+
+ /**
+ *
+ * @private
+ */
+ _onDrawStop(delegate) {}
+
+ /**
+ *
+ * @param positions
+ * @private
+ */
+ _onCalc(positions) {}
+
+ /**
+ *
+ * @param measure
+ * @param options
+ */
+ _startHook(measure, options) {
+ this._viewer = measure.viewer
+ this._layer = measure.layer
+ this._options = options
+ this._options.onDrawStop = this._onDrawStop.bind(this)
+ this._options.onCalc = this._onCalc.bind(this)
+ this._layer.entities.add(this._startLabel)
+ this._layer.entities.add(this._resultLabel)
+ }
+
+ /**
+ *
+ * @param measure
+ * @param options
+ * @returns {MeasureBase}
+ */
+ start(measure, options) {
+ return this
+ }
+}
+
+export default MeasureBase
diff --git a/src/modules/measure/MeasureType.js b/src/modules/measure/MeasureType.js
new file mode 100644
index 00000000..f68d960b
--- /dev/null
+++ b/src/modules/measure/MeasureType.js
@@ -0,0 +1,18 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-07-11 09:56:33
+ */
+
+const MeasureType = {
+ ANGLE: 'angle',
+ AREA: 'area',
+ AREA_HEIGHT: 'area_height',
+ AREA_SURFACE: 'area_surface',
+ DISTANCE: 'distance',
+ DISTANCE_SURFACE: 'diatance_surface',
+ HEADING: 'heading',
+ HEIGHT: 'height',
+ TRIANGLE_HEIGHT: 'triangle_height'
+}
+
+export default MeasureType
diff --git a/src/modules/measure/draw/Draw.js b/src/modules/measure/draw/Draw.js
new file mode 100644
index 00000000..0591bce7
--- /dev/null
+++ b/src/modules/measure/draw/Draw.js
@@ -0,0 +1,96 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-31 19:45:32
+ */
+
+import { PlotEventType } from '@dc-modules/event'
+
+class Draw {
+ constructor(style) {
+ this._style = style
+ this._viewer = undefined
+ this._layer = undefined
+ this._delegate = undefined
+ this._options = {}
+ this._positions = []
+ }
+
+ get drawTool() {
+ return this._viewer.drawTool
+ }
+
+ /**
+ * The hook for mount layer
+ * Subclasses need to be overridden
+ * @private
+ */
+ _mountedHook() {}
+
+ /**
+ *
+ * @param position
+ * @private
+ */
+ _onDrawAnchor(position) {}
+
+ /**
+ *
+ * @param position
+ * @private
+ */
+ _onAnchorMoving(position) {
+ this._positions.pop()
+ this._positions.push(position)
+ this._options.onCalc && this._options.onCalc(this._positions)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _onDrawStop() {
+ this._unbindEvent()
+ this._viewer.drawTool.deactivate()
+ this._options.onDrawStop && this._options.onDrawStop(this._delegate)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _bindEvent() {
+ this.drawTool.on(PlotEventType.DRAW_ANCHOR, this._onDrawAnchor, this)
+ this.drawTool.on(PlotEventType.ANCHOR_MOVING, this._onAnchorMoving, this)
+ this.drawTool.on(PlotEventType.DRAW_STOP, this._onDrawStop, this)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _unbindEvent() {
+ this.drawTool.off(PlotEventType.DRAW_ANCHOR, this._onDrawAnchor, this)
+ this.drawTool.off(PlotEventType.ANCHOR_MOVING, this._onAnchorMoving, this)
+ this.drawTool.off(PlotEventType.DRAW_STOP, this._onDrawStop, this)
+ }
+
+ /**
+ *
+ * @param measure
+ * @param options
+ * @returns {Draw}
+ */
+ start(measure, options) {
+ this._viewer = measure.viewer
+ this._layer = measure.layer
+ this._options = options
+ this._viewer.editTool.deactivate()
+ this._viewer.drawTool.activate(options)
+ this._mountedHook()
+ this._unbindEvent()
+ this._bindEvent()
+ return this
+ }
+}
+
+export default Draw
diff --git a/src/modules/measure/draw/DrawPolygon.js b/src/modules/measure/draw/DrawPolygon.js
new file mode 100644
index 00000000..65a3da83
--- /dev/null
+++ b/src/modules/measure/draw/DrawPolygon.js
@@ -0,0 +1,47 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-29 20:55:14
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+import { PlotEventType } from '@dc-modules/event'
+import Draw from './Draw'
+
+class DrawPolygon extends Draw {
+ constructor(style) {
+ super(style)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountedHook() {
+ this.drawTool.tooltipMess = '左击选择点位,右击结束'
+ this._delegate = new Cesium.Entity({
+ polygon: {
+ ...this._style,
+ hierarchy: new Cesium.CallbackProperty(() => {
+ if (this._positions.length > 2) {
+ return new Cesium.PolygonHierarchy(this._positions)
+ } else {
+ return null
+ }
+ }, false),
+ },
+ })
+ this._layer.entities.add(this._delegate)
+ }
+
+ /**
+ *
+ * @param position
+ * @private
+ */
+ _onDrawAnchor(position) {
+ this._positions.push(position)
+ this.drawTool.fire(PlotEventType.CREATE_ANCHOR, { position })
+ }
+}
+
+export default DrawPolygon
diff --git a/src/modules/measure/draw/DrawPolyline.js b/src/modules/measure/draw/DrawPolyline.js
new file mode 100644
index 00000000..0ef3eab8
--- /dev/null
+++ b/src/modules/measure/draw/DrawPolyline.js
@@ -0,0 +1,51 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-29 20:54:37
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+import { PlotEventType } from '@dc-modules/event'
+import Draw from './Draw'
+
+class DrawPolyline extends Draw {
+ constructor(style) {
+ super(style)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountedHook() {
+ this.drawTool.tooltipMess = '左击选择点位,右击结束'
+ this._delegate = new Cesium.Entity({
+ polyline: {
+ ...this._style,
+ positions: new Cesium.CallbackProperty(() => {
+ return this._positions
+ }, false),
+ },
+ })
+ this._layer.entities.add(this._delegate)
+ }
+
+ /**
+ *
+ * @param position
+ * @returns {boolean}
+ * @private
+ */
+ _onDrawAnchor(position) {
+ let len = this._positions.length
+ this._positions.push(position)
+ if (len > 0) {
+ this.drawTool.fire(PlotEventType.CREATE_ANCHOR, { position })
+ }
+ if (len >= this._options.maxAnchorSize) {
+ this._positions.pop()
+ this.drawTool.fire(PlotEventType.DRAW_STOP, position)
+ }
+ }
+}
+
+export default DrawPolyline
diff --git a/src/modules/measure/edit/Edit.js b/src/modules/measure/edit/Edit.js
new file mode 100644
index 00000000..c6fa00c6
--- /dev/null
+++ b/src/modules/measure/edit/Edit.js
@@ -0,0 +1,113 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-30 23:50:53
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+import { PlotEventType } from '@dc-modules/event'
+
+class Edit {
+ constructor(overlay) {
+ this._viewer = undefined
+ this._layer = undefined
+ this._overlay = overlay
+ this._delegate = new Cesium.Entity()
+ this._delegate.merge(overlay)
+ this._options = {}
+ this._positions = []
+ }
+
+ get editTool() {
+ return this._viewer.editTool
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountedHook() {}
+
+ /**
+ *
+ * @private
+ */
+ _mountAnchor() {}
+
+ /**
+ *
+ * @param index
+ * @param position
+ * @private
+ */
+ _onAnchorMoving({ pickedAnchor, position }) {}
+
+ /**
+ *
+ * @param pickedAnchor
+ * @param position
+ * @private
+ */
+ _onEditAnchorStop({ pickedAnchor, position }) {}
+
+ /**
+ *
+ * @param pickedAnchor
+ * @param position
+ * @private
+ */
+ _onEditStop({ pickedAnchor, position }) {
+ this._unbindEvent()
+ this._viewer.editTool.deactivate()
+ }
+
+ /**
+ *
+ * @returns {Edit}
+ * @private
+ */
+ _bindEvent() {
+ this.editTool.on(PlotEventType.ANCHOR_MOVING, this._onAnchorMoving, this)
+ this.editTool.on(
+ PlotEventType.EDIT_ANCHOR_STOP,
+ this._onEditAnchorStop,
+ this
+ )
+ this.editTool.on(PlotEventType.EDIT_STOP, this._onEditStop, this)
+ return this
+ }
+
+ /**
+ *
+ * @private
+ */
+ _unbindEvent() {
+ this.editTool.off(PlotEventType.ANCHOR_MOVING, this._onAnchorMoving, this)
+ this.editTool.off(
+ PlotEventType.EDIT_ANCHOR_STOP,
+ this._onEditAnchorStop,
+ this
+ )
+ this.editTool.off(PlotEventType.EDIT_STOP, this._onEditStop, this)
+ }
+
+ /**
+ *
+ * @param measure
+ * @param options
+ * @returns {Edit}
+ */
+ start(measure, options) {
+ this._viewer = measure.viewer
+ this._layer = measure.layer
+ this._options = options
+ this._viewer.editTool.deactivate()
+ this._viewer.editTool.activate(options)
+ this._mountedHook()
+ this._mountAnchor()
+ this._unbindEvent()
+ this._bindEvent()
+ return this
+ }
+}
+
+export default Edit
diff --git a/src/modules/measure/edit/EditPolygon.js b/src/modules/measure/edit/EditPolygon.js
new file mode 100644
index 00000000..dbf07e7a
--- /dev/null
+++ b/src/modules/measure/edit/EditPolygon.js
@@ -0,0 +1,163 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-30 23:12:09
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+import { PlotEventType } from '@dc-modules/event'
+import { midCartesian } from '@dc-modules/math'
+import Edit from './Edit'
+
+class EditPolygon extends Edit {
+ constructor(overlay) {
+ super(overlay)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountedHook() {
+ this.editTool.tooltipMess = '点击锚点移动,右击结束编辑'
+ this._delegate.polygon.hierarchy = new Cesium.CallbackProperty(time => {
+ if (this._positions.length > 2) {
+ return new Cesium.PolygonHierarchy(this._positions)
+ } else {
+ return null
+ }
+ }, false)
+ this._layer.entities.add(this._delegate)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountAnchor() {
+ let positions = [].concat(
+ this._overlay.polygon.hierarchy.getValue(Cesium.JulianDate.now())
+ .positions
+ )
+ positions.push(positions[0])
+ for (let i = 0; i < positions.length - 1; i++) {
+ let mid = midCartesian(positions[i], positions[i + 1])
+ this._positions.push(positions[i])
+ this._positions.push(mid)
+ }
+ this._positions.forEach((item, index) => {
+ this.editTool.fire(PlotEventType.CREATE_ANCHOR, {
+ position: item,
+ index: index,
+ isMid: index % 2 !== 0
+ })
+ })
+ this._layer.entities.remove(this._overlay)
+ }
+
+ /**
+ *
+ * @param pickedAnchor
+ * @param position
+ * @returns {boolean}
+ * @private
+ */
+ _onEditAnchorStop({ pickedAnchor, position }) {
+ let properties = pickedAnchor.properties.getValue(Cesium.JulianDate.now())
+ let currentIndex = properties.index
+ if (properties.isMid) {
+ let preMidPosition
+ let nextMidPosition
+ let len = this._positions.length
+ if (currentIndex === len - 1) {
+ preMidPosition = midCartesian(
+ this._positions[currentIndex],
+ this._positions[currentIndex - 1]
+ )
+ nextMidPosition = midCartesian(
+ this._positions[currentIndex],
+ this._positions[0]
+ )
+ } else {
+ preMidPosition = midCartesian(
+ this._positions[currentIndex],
+ this._positions[currentIndex - 1]
+ )
+ nextMidPosition = midCartesian(
+ this._positions[currentIndex],
+ this._positions[currentIndex + 1]
+ )
+ }
+ this._positions.splice(
+ currentIndex,
+ 1,
+ preMidPosition,
+ position,
+ nextMidPosition
+ )
+ this.editTool.fire(PlotEventType.CLEAR_ANCHOR)
+ this._positions.forEach((item, index) => {
+ this.editTool.fire(PlotEventType.CREATE_ANCHOR, {
+ position: item,
+ index: index,
+ isMid: index % 2 !== 0
+ })
+ })
+ }
+ }
+
+ /**
+ *
+ * @param pickedAnchor
+ * @param position
+ * @private
+ */
+ _onAnchorMoving({ pickedAnchor, position }) {
+ let properties = pickedAnchor.properties.getValue(Cesium.JulianDate.now())
+ let currentIndex = properties.index
+ this._positions[currentIndex] = position
+ let len = this._positions.length
+ if (!properties.isMid) {
+ let preAnchorIndex = -1
+ let preMidAnchorIndex = -1
+ let nextAnchorIndex = -1
+ let nextMidAnchorIndex = -1
+ if (currentIndex === 0) {
+ preAnchorIndex = len - 2
+ preMidAnchorIndex = len - 1
+ nextAnchorIndex = currentIndex + 2
+ nextMidAnchorIndex = currentIndex + 1
+ } else if (currentIndex === len - 2) {
+ preAnchorIndex = currentIndex - 2
+ preMidAnchorIndex = currentIndex - 1
+ nextAnchorIndex = 0
+ nextMidAnchorIndex = len - 1
+ } else {
+ preAnchorIndex = currentIndex - 2
+ preMidAnchorIndex = currentIndex - 1
+ nextAnchorIndex = currentIndex + 2
+ nextMidAnchorIndex = currentIndex + 1
+ }
+ let preMidPosition = midCartesian(
+ this._positions[preAnchorIndex],
+ this._positions[currentIndex]
+ )
+ let nextMidPosition = midCartesian(
+ this._positions[nextAnchorIndex],
+ this._positions[currentIndex]
+ )
+ this._positions[preMidAnchorIndex] = preMidPosition
+ this._positions[nextMidAnchorIndex] = nextMidPosition
+ this.editTool.fire(PlotEventType.UPDATE_ANCHOR, {
+ index: preMidAnchorIndex,
+ position: preMidPosition
+ })
+ this.editTool.fire(PlotEventType.UPDATE_ANCHOR, {
+ index: nextMidAnchorIndex,
+ position: nextMidPosition
+ })
+ }
+ this._options.onCalc && this._options.onCalc(this._positions)
+ }
+}
+
+export default EditPolygon
diff --git a/src/modules/measure/edit/EditPolyline.js b/src/modules/measure/edit/EditPolyline.js
new file mode 100644
index 00000000..c91f403d
--- /dev/null
+++ b/src/modules/measure/edit/EditPolyline.js
@@ -0,0 +1,161 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-30 22:39:34
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+import { PlotEventType } from '@dc-modules/event'
+import { midCartesian } from '@dc-modules/math'
+import Edit from './Edit'
+
+class EditPolyline extends Edit {
+ constructor(overlay) {
+ super(overlay)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountedHook() {
+ this.editTool.tooltipMess = '点击锚点移动,右击结束编辑'
+ this._delegate.polyline.positions = new Cesium.CallbackProperty(() => {
+ if (this._positions.length > 1) {
+ return this._positions
+ } else {
+ return null
+ }
+ }, false)
+ this._layer.entities.add(this._delegate)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountAnchor() {
+ let positions = [].concat(
+ this._overlay.polyline.positions.getValue(Cesium.JulianDate.now())
+ )
+ if (this._options.maxAnchorSize > 2) {
+ for (let i = 0; i < positions.length - 1; i++) {
+ let mid = midCartesian(positions[i], positions[i + 1])
+ this._positions.push(positions[i])
+ this._positions.push(mid)
+ }
+ this._positions.push(positions[positions.length - 1])
+ this._positions.forEach((item, index) => {
+ this.editTool.fire(PlotEventType.CREATE_ANCHOR, {
+ position: item,
+ index: index,
+ isMid: index % 2 !== 0
+ })
+ })
+ } else {
+ this._positions = positions
+ this._positions.forEach((item, index) => {
+ this.editTool.fire(PlotEventType.CREATE_ANCHOR, {
+ position: item,
+ index: index
+ })
+ })
+ }
+ this._layer.entities.remove(this._overlay)
+ }
+
+ /**
+ *
+ * @param pickedAnchor
+ * @param position
+ * @returns {boolean}
+ * @private
+ */
+ _onEditAnchorStop({ pickedAnchor, position }) {
+ let properties = pickedAnchor.properties.getValue(Cesium.JulianDate.now())
+ let currentIndex = properties.index
+ if (properties.isMid) {
+ let preMidPosition = midCartesian(
+ this._positions[currentIndex],
+ this._positions[currentIndex - 1]
+ )
+ let nextMidPosition = midCartesian(
+ this._positions[currentIndex],
+ this._positions[currentIndex + 1]
+ )
+ this._positions.splice(
+ currentIndex,
+ 1,
+ preMidPosition,
+ position,
+ nextMidPosition
+ )
+ this.editTool.fire(PlotEventType.CLEAR_ANCHOR)
+ this._positions.forEach((item, index) => {
+ this.editTool.fire(PlotEventType.CREATE_ANCHOR, {
+ position: item,
+ index: index,
+ isMid: index % 2 !== 0
+ })
+ })
+ }
+ }
+
+ /**
+ *
+ * @param pickedAnchor
+ * @param position
+ * @private
+ */
+ _onAnchorMoving({ pickedAnchor, position }) {
+ let properties = pickedAnchor.properties.getValue(Cesium.JulianDate.now())
+ let currentIndex = properties.index
+ this._positions[currentIndex] = position
+ if (!properties.isMid && this._options.maxAnchorSize > 2) {
+ let preAnchorIndex = -1
+ let preMidAnchorIndex = -1
+ let nextAnchorIndex = -1
+ let nextMidAnchorIndex = -1
+ let len = this._positions.length
+
+ if (currentIndex === 0) {
+ nextAnchorIndex = currentIndex + 2
+ nextMidAnchorIndex = currentIndex + 1
+ } else if (properties.index === len - 1) {
+ preAnchorIndex = currentIndex - 2
+ preMidAnchorIndex = currentIndex - 1
+ } else {
+ preAnchorIndex = currentIndex - 2
+ preMidAnchorIndex = currentIndex - 1
+ nextAnchorIndex = currentIndex + 2
+ nextMidAnchorIndex = currentIndex + 1
+ }
+
+ if (preAnchorIndex > 0) {
+ let preMidPosition = midCartesian(
+ this._positions[preAnchorIndex],
+ this._positions[currentIndex]
+ )
+ this._positions[preMidAnchorIndex] = preMidPosition
+ this.editTool.fire(PlotEventType.UPDATE_ANCHOR, {
+ index: preMidAnchorIndex,
+ position: preMidPosition
+ })
+ }
+
+ if (nextAnchorIndex > 0) {
+ let nextMidPosition = midCartesian(
+ this._positions[nextAnchorIndex],
+ this._positions[currentIndex]
+ )
+ this._positions[nextMidAnchorIndex] = nextMidPosition
+ this.editTool.fire(PlotEventType.UPDATE_ANCHOR, {
+ index: nextMidAnchorIndex,
+ position: nextMidPosition
+ })
+ }
+ }
+ this._options.onCalc && this._options.onCalc(this._positions)
+ }
+}
+
+export default EditPolyline
diff --git a/src/modules/measure/index.js b/src/modules/measure/index.js
new file mode 100644
index 00000000..fd0d9223
--- /dev/null
+++ b/src/modules/measure/index.js
@@ -0,0 +1,7 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-07-11 09:56:33
+ */
+
+export { default as MeasureType } from './MeasureType'
+export { default as Measure } from './Measure'
diff --git a/src/modules/measure/type/Angle.js b/src/modules/measure/type/Angle.js
new file mode 100644
index 00000000..61b75ffb
--- /dev/null
+++ b/src/modules/measure/type/Angle.js
@@ -0,0 +1,117 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-07-11 09:56:33
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+import DrawPolyline from '../draw/DrawPolyline'
+import EditPolyline from '../edit/EditPolyline'
+import MeasureBase from '../MeasureBase'
+
+class Angle extends MeasureBase {
+ constructor() {
+ super()
+ this._maxAnchorSize = 2
+ this._helpLinePositions = []
+ this._helpLine = new Cesium.Entity({
+ polyline: {
+ positions: new Cesium.CallbackProperty(() => {
+ if (this._helpLinePositions.length > 1) {
+ return this._helpLinePositions
+ } else {
+ return null
+ }
+ }, false),
+ },
+ })
+ }
+
+ /**
+ *
+ * @param delegate
+ * @private
+ */
+ _onDrawStop(delegate) {
+ new EditPolyline(delegate).start(
+ {
+ viewer: this._viewer,
+ layer: this._layer,
+ },
+ {
+ ...this._options,
+ ...{ maxAnchorSize: this._maxAnchorSize },
+ }
+ )
+ }
+
+ /**
+ *
+ * @param positions
+ * @private
+ */
+ _onCalc(positions) {
+ if (positions.length > 0) {
+ this._startLabel.position = positions[0]
+ }
+ if (positions.length > 1) {
+ this._resultLabel.position = positions[1]
+
+ let up = Cesium.Ellipsoid.WGS84.geodeticSurfaceNormal(
+ positions[0],
+ new Cesium.Cartesian3()
+ )
+
+ let plane = Cesium.Plane.fromPointNormal(positions[0], up)
+
+ this._helpLinePositions = [
+ positions[0],
+ Cesium.Plane.projectPointOntoPlane(
+ plane,
+ positions[1],
+ new Cesium.Cartesian3()
+ ),
+ positions[1],
+ ]
+
+ let hegiht = Math.abs(Cesium.Plane.getPointDistance(plane, positions[1]))
+
+ this._resultLabel.label.text = `角度:${Cesium.Math.toDegrees(
+ Math.asin(
+ hegiht / Cesium.Cartesian3.distance(positions[0], positions[1])
+ )
+ ).toFixed(2)} 度`
+ }
+ }
+
+ /**
+ *
+ * @param measure
+ * @param options
+ * @returns {Angle}
+ */
+ start(measure, options) {
+ this._startHook(measure, options)
+ let helpLineMaterial = new Cesium.PolylineDashMaterialProperty({
+ color: Cesium.Color.GREENYELLOW,
+ })
+ this._helpLine.polyline.material = helpLineMaterial
+ this._helpLine.polyline.depthFailMaterial = helpLineMaterial
+ this._layer.entities.add(this._helpLine)
+ new DrawPolyline({
+ material: options.material || Cesium.Color.YELLOW.withAlpha(0.6),
+ depthFailMaterial:
+ options.depthFailMaterial ||
+ new Cesium.PolylineDashMaterialProperty({
+ color: Cesium.Color.YELLOW.withAlpha(0.6),
+ }),
+ width: options.width || 2,
+ clampToGround: false,
+ }).start(measure, {
+ ...options,
+ ...{ maxAnchorSize: this._maxAnchorSize },
+ })
+ return this
+ }
+}
+
+export default Angle
diff --git a/src/modules/measure/type/Area.js b/src/modules/measure/type/Area.js
new file mode 100644
index 00000000..d1edabd4
--- /dev/null
+++ b/src/modules/measure/type/Area.js
@@ -0,0 +1,64 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-07-11 09:56:33
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+import { area } from '@dc-modules/math'
+import DrawPolygon from '../draw/DrawPolygon'
+import EditPolygon from '../edit/EditPolygon'
+import MeasureBase from '../MeasureBase'
+
+class Area extends MeasureBase {
+ constructor() {
+ super()
+ }
+
+ /**
+ *
+ * @param entity
+ * @private
+ */
+ _onDrawStop(entity) {
+ new EditPolygon(entity).start(
+ {
+ viewer: this._viewer,
+ layer: this._layer
+ },
+ this._options
+ )
+ }
+
+ /**
+ *
+ * @param positions
+ * @private
+ */
+ _onCalc(positions) {
+ if (positions.length > 2) {
+ this._resultLabel.position = Cesium.BoundingSphere.fromPoints(
+ positions
+ )?.center
+ this._resultLabel.label.text = `面积:${area(positions).toFixed(
+ 2
+ )} 平方米`
+ }
+ }
+
+ /**
+ *
+ * @param measure
+ * @param options
+ * @returns {Area}
+ */
+ start(measure, options) {
+ this._startHook(measure, options)
+ new DrawPolygon({
+ material: options.material || Cesium.Color.YELLOW.withAlpha(0.6),
+ perPositionHeight: true
+ }).start(measure, this._options)
+ return this
+ }
+}
+
+export default Area
diff --git a/src/modules/measure/type/AreaHeight.js b/src/modules/measure/type/AreaHeight.js
new file mode 100644
index 00000000..90eec327
--- /dev/null
+++ b/src/modules/measure/type/AreaHeight.js
@@ -0,0 +1,122 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-07-11 09:56:33
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+import DrawPolygon from '../draw/DrawPolygon'
+import EditPolygon from '../edit/EditPolygon'
+import MeasureBase from '../MeasureBase'
+
+class AreaHeight extends MeasureBase {
+ constructor() {
+ super()
+ }
+
+ /**
+ *
+ * @param entity
+ * @private
+ */
+ _onDrawStop(entity) {
+ new EditPolygon(entity).start(
+ {
+ viewer: this._viewer,
+ layer: this._layer
+ },
+ this._options
+ )
+ }
+
+ /**
+ *
+ * @param rectangle
+ * @param num
+ * @returns {*[]}
+ * @private
+ */
+ _lerp(rectangle, num = 4) {
+ let lerpPositions = []
+ for (let y = 0; y < num; ++y) {
+ for (let x = 0; x < num; ++x) {
+ let longitude = Cesium.Math.lerp(
+ rectangle.west,
+ rectangle.east,
+ x / (num - 1)
+ )
+ let latitude = Cesium.Math.lerp(
+ rectangle.south,
+ rectangle.north,
+ y / (num - 1)
+ )
+ lerpPositions.push(Cesium.Cartesian3.fromRadians(longitude, latitude))
+ }
+ }
+ return lerpPositions
+ }
+
+ /**
+ *
+ * @param positions
+ * @private
+ */
+ _onCalc(positions) {
+ if (positions.length > 2) {
+ let lerpPositions = this._lerp(
+ Cesium.Rectangle.fromCartesianArray(positions),
+ this._options.lerpNum
+ )
+
+ this._getSampledHeight(lerpPositions, true)
+ .then(([updatedCartographics, updatedCartesians]) => {
+ return updatedCartographics.map((item, index) => {
+ return {
+ lng: item.longitude,
+ lat: item.latitude,
+ alt: Math.max(
+ item.height || 0,
+ updatedCartesians[index]
+ ? Cesium.Cartographic.fromCartesian(updatedCartesians[index])
+ .height
+ : 0
+ )
+ }
+ })
+ })
+ .then(positions => {
+ let max = 0
+ let position = undefined
+ positions.forEach(item => {
+ if (item.alt > max) {
+ max = item.alt
+ position = item
+ }
+ })
+ if (position) {
+ this._resultLabel.position = Cesium.Cartesian3.fromRadians(
+ position.lng,
+ position.lat,
+ position.alt
+ )
+ this._resultLabel.label.text = `高度:${max.toFixed(2)} 米`
+ }
+ })
+ }
+ }
+
+ /**
+ *
+ * @param measure
+ * @param options
+ * @returns {AreaHeight}
+ */
+ start(measure, options) {
+ this._startHook(measure, options)
+ new DrawPolygon({
+ material: options.material || Cesium.Color.YELLOW.withAlpha(0.6)
+ }).start(measure, this._options)
+ return this
+ }
+}
+
+export default AreaHeight
diff --git a/src/modules/measure/type/AreaSurface.js b/src/modules/measure/type/AreaSurface.js
new file mode 100644
index 00000000..59ad2c1c
--- /dev/null
+++ b/src/modules/measure/type/AreaSurface.js
@@ -0,0 +1,108 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-07-11 09:56:33
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+import { area } from '@dc-modules/math'
+import EditPolygon from '../edit/EditPolygon'
+import DrawPolygon from '../draw/DrawPolygon'
+import MeasureBase from '../MeasureBase'
+
+class AreaSurface extends MeasureBase {
+ constructor() {
+ super()
+ }
+
+ /**
+ *
+ * @param rectangle
+ * @param num
+ * @returns {*[]}
+ * @private
+ */
+ _lerp(rectangle, num = 4) {
+ let lerpPositions = []
+ for (let y = 0; y < num; ++y) {
+ for (let x = 0; x < num; ++x) {
+ let longitude = Cesium.Math.lerp(
+ rectangle.west,
+ rectangle.east,
+ x / (num - 1)
+ )
+ let latitude = Cesium.Math.lerp(
+ rectangle.south,
+ rectangle.north,
+ y / (num - 1)
+ )
+ lerpPositions.push(Cesium.Cartesian3.fromRadians(longitude, latitude))
+ }
+ }
+ return lerpPositions
+ }
+
+ /**
+ *
+ * @param entity
+ * @private
+ */
+ _onDrawStop(entity) {
+ new EditPolygon(entity).start(
+ {
+ viewer: this._viewer,
+ layer: this._layer
+ },
+ this._options
+ )
+ }
+
+ /**
+ *
+ * @param positions
+ * @private
+ */
+ _onCalc(positions) {
+ if (positions.length > 2) {
+ this._resultLabel.position = Cesium.BoundingSphere.fromPoints(
+ positions
+ )?.center
+
+ let lerpPositions = this._lerp(
+ Cesium.Rectangle.fromCartesianArray(positions),
+ this._options.lerpNum
+ )
+
+ this._getSampledHeight(lerpPositions.concat(positions), false)
+ .then(([updatedCartographics]) => {
+ return updatedCartographics.map(item =>
+ Cesium.Cartesian3.fromDegrees(
+ Cesium.Math.toDegrees(item.longitude),
+ Cesium.Math.toDegrees(item.latitude),
+ item.height
+ )
+ )
+ })
+ .then(positions => {
+ this._resultLabel.label.text = `面积:${area(positions).toFixed(
+ 2
+ )} 平方米`
+ })
+ }
+ }
+
+ /**
+ *
+ * @param measure
+ * @param options
+ * @returns {AreaSurface}
+ */
+ start(measure, options) {
+ this._startHook(measure, options)
+ new DrawPolygon({
+ material: options.material || Cesium.Color.YELLOW.withAlpha(0.6)
+ }).start(measure, this._options)
+ return this
+ }
+}
+
+export default AreaSurface
diff --git a/src/modules/measure/type/Distance.js b/src/modules/measure/type/Distance.js
new file mode 100644
index 00000000..e89f243d
--- /dev/null
+++ b/src/modules/measure/type/Distance.js
@@ -0,0 +1,83 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-07-11 09:56:33
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+import DrawPolyline from '../draw/DrawPolyline'
+import EditPolyline from '../edit/EditPolyline'
+import MeasureBase from '../MeasureBase'
+
+class Distance extends MeasureBase {
+ constructor() {
+ super()
+ this._maxAnchorSize = 9999
+ }
+
+ /**
+ *
+ * @param entity
+ * @private
+ */
+ _onDrawStop(entity) {
+ new EditPolyline(entity).start(
+ {
+ viewer: this._viewer,
+ layer: this._layer
+ },
+ {
+ ...this._options,
+ ...{ maxAnchorSize: this._maxAnchorSize }
+ }
+ )
+ }
+
+ /**
+ *
+ * @param positions
+ * @private
+ */
+ _onCalc(positions) {
+ if (positions.length > 0) {
+ this._startLabel.position = positions[0]
+ }
+ if (positions.length > 1) {
+ this._resultLabel.position = positions[positions.length - 1]
+ let sum = 0
+ for (let i = 0; i < positions.length - 1; i++) {
+ let s = Cesium.Cartesian3.distance(positions[i], positions[i + 1])
+ sum += s
+ }
+ this._resultLabel.label.text =
+ sum > 1000
+ ? `距离:${(sum / 1000).toFixed(2)} 公里`
+ : `距离:${sum.toFixed(2)} 米`
+ }
+ }
+
+ /**
+ *
+ * @param measure
+ * @param options
+ * @returns {Distance}
+ */
+ start(measure, options) {
+ this._startHook(measure, options)
+ new DrawPolyline({
+ material: options.material || Cesium.Color.YELLOW.withAlpha(0.6),
+ depthFailMaterial:
+ options.depthFailMaterial ||
+ new Cesium.PolylineDashMaterialProperty({
+ color: Cesium.Color.YELLOW.withAlpha(0.6)
+ }),
+ width: options.width || 2,
+ clampToGround: false
+ }).start(measure, {
+ ...options,
+ ...{ maxAnchorSize: this._maxAnchorSize }
+ })
+ return this
+ }
+}
+
+export default Distance
diff --git a/src/modules/measure/type/DistanceSurface.js b/src/modules/measure/type/DistanceSurface.js
new file mode 100644
index 00000000..c507930e
--- /dev/null
+++ b/src/modules/measure/type/DistanceSurface.js
@@ -0,0 +1,133 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-07-11 09:56:33
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+import DrawPolyline from '../draw/DrawPolyline'
+import EditPolyline from '../edit/EditPolyline'
+import MeasureBase from '../MeasureBase'
+
+class DistanceSurface extends MeasureBase {
+ constructor() {
+ super()
+ this._maxAnchorSize = 9999
+ }
+
+ /**
+ *
+ * @param start
+ * @param end
+ * @param num
+ * @returns {*[]}
+ * @private
+ */
+ _lerp(start, end, num = 5) {
+ let lerpPositions = []
+ let c1 = Cesium.Cartographic.fromCartesian(start)
+ let c2 = Cesium.Cartographic.fromCartesian(end)
+ for (let i = 0; i < num; i++) {
+ let lng = Cesium.Math.lerp(c1.longitude, c2.longitude, i / num)
+ let lat = Cesium.Math.lerp(c1.latitude, c2.latitude, i / num)
+ let alt = c1.height - (c1.height - c2.height) * (i / num)
+ lerpPositions.push(Cesium.Cartesian3.fromRadians(lng, lat, alt))
+ }
+ return lerpPositions
+ }
+
+ /**
+ *
+ * @param entity
+ * @private
+ */
+ _onDrawStop(entity) {
+ new EditPolyline(entity).start(
+ {
+ viewer: this._viewer,
+ layer: this._layer
+ },
+ {
+ ...this._options,
+ ...{ maxAnchorSize: this._maxAnchorSize }
+ }
+ )
+ }
+
+ /**
+ *
+ * @param positions
+ * @private
+ */
+ _onCalc(positions) {
+ if (positions.length > 0) {
+ this._startLabel.position = positions[0]
+ }
+ if (positions.length > 1) {
+ let lerpPositions = [positions[0]]
+ this._resultLabel.position = positions[positions.length - 1]
+ for (let i = 0; i < positions.length - 1; i++) {
+ lerpPositions = lerpPositions.concat(
+ this._lerp(positions[i], positions[i + 1], this._options.lerpNum)
+ )
+ }
+ lerpPositions.push(positions[positions.length - 1])
+ this._getSampledHeight(lerpPositions)
+ .then(([updatedCartographics, updatedCartesians]) => {
+ return updatedCartographics.map((item, index) =>
+ Cesium.Cartesian3.fromDegrees(
+ Cesium.Math.toDegrees(item.longitude),
+ Cesium.Math.toDegrees(item.latitude),
+ Math.max(
+ item.height || 0,
+ updatedCartesians[index]
+ ? Cesium.Cartographic.fromCartesian(updatedCartesians[index])
+ .height
+ : 0
+ )
+ )
+ )
+ })
+ .then(positions => {
+ let sum = 0
+ for (let i = 0; i < positions.length - 1; i++) {
+ let s = Cesium.Cartesian3.distance(positions[i], positions[i + 1])
+ sum += s
+ }
+ this._resultLabel.label.text =
+ sum > 1000
+ ? `距离:${(sum / 1000).toFixed(2)} 公里`
+ : `距离:${sum.toFixed(2)} 米`
+ })
+ }
+ }
+
+ /**
+ *
+ * @param measure
+ * @param options
+ * @returns {DistanceSurface}
+ */
+ start(measure, options) {
+ this._startHook(measure, options)
+ this._startLabel.label.heightReference =
+ Cesium.HeightReference.CLAMP_TO_GROUND
+ this._resultLabel.label.heightReference =
+ Cesium.HeightReference.CLAMP_TO_GROUND
+ new DrawPolyline({
+ material: options.material || Cesium.Color.YELLOW.withAlpha(0.6),
+ depthFailMaterial:
+ options.depthFailMaterial ||
+ new Cesium.PolylineDashMaterialProperty({
+ color: Cesium.Color.YELLOW.withAlpha(0.6)
+ }),
+ width: options.width || 2,
+ clampToGround: true
+ }).start(measure, {
+ ...this._options,
+ ...{ maxAnchorSize: this._maxAnchorSize }
+ })
+ return this
+ }
+}
+
+export default DistanceSurface
diff --git a/src/modules/measure/type/Heading.js b/src/modules/measure/type/Heading.js
new file mode 100644
index 00000000..c5134478
--- /dev/null
+++ b/src/modules/measure/type/Heading.js
@@ -0,0 +1,77 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-07-11 09:56:33
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+import { heading } from '@dc-modules/math'
+import DrawPolyline from '../draw/DrawPolyline'
+import EditPolyline from '../edit/EditPolyline'
+import MeasureBase from '../MeasureBase'
+
+class Heading extends MeasureBase {
+ constructor() {
+ super()
+ this._maxAnchorSize = 2
+ }
+
+ /**
+ *
+ * @param entity
+ * @private
+ */
+ _onDrawStop(entity) {
+ new EditPolyline(entity).start(
+ {
+ viewer: this._viewer,
+ layer: this._layer
+ },
+ {
+ ...this._options,
+ ...{ maxAnchorSize: this._maxAnchorSize }
+ }
+ )
+ }
+
+ /**
+ *
+ * @param positions
+ * @private
+ */
+ _onCalc(positions) {
+ if (positions.length > 0) {
+ this._startLabel.position = positions[0]
+ }
+ if (positions.length > 1) {
+ this._resultLabel.position = positions[positions.length - 1]
+ this._resultLabel.label.text = `偏航:${Cesium.Math.toDegrees(
+ heading(positions[0], positions[1])
+ ).toFixed(1)} 度`
+ }
+ }
+
+ /**
+ *
+ * @param measure
+ * @param options
+ * @returns {Heading}
+ */
+ start(measure, options) {
+ this._startHook(measure, options)
+ new DrawPolyline({
+ material: options.material || Cesium.Color.YELLOW.withAlpha(0.6),
+ depthFailMaterial:
+ options.depthFailMaterial ||
+ new Cesium.PolylineDashMaterialProperty({
+ color: Cesium.Color.YELLOW.withAlpha(0.6)
+ }),
+ width: options.width || 2
+ }).start(measure, {
+ ...this._options,
+ ...{ maxAnchorSize: this._maxAnchorSize }
+ })
+ return this
+ }
+}
+
+export default Heading
diff --git a/src/modules/measure/type/Height.js b/src/modules/measure/type/Height.js
new file mode 100644
index 00000000..ed01bc0c
--- /dev/null
+++ b/src/modules/measure/type/Height.js
@@ -0,0 +1,112 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-07-11 09:56:33
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+import DrawPolyline from '../draw/DrawPolyline'
+import EditPolyline from '../edit/EditPolyline'
+import MeasureBase from '../MeasureBase'
+
+class Height extends MeasureBase {
+ constructor() {
+ super()
+ this._maxAnchorSize = 2
+ this._helpLinePositions = []
+ this._helpLine = new Cesium.Entity({
+ polyline: {
+ positions: new Cesium.CallbackProperty(() => {
+ if (this._helpLinePositions.length > 1) {
+ return this._helpLinePositions
+ } else {
+ return null
+ }
+ }, false)
+ }
+ })
+ }
+
+ /**
+ *
+ * @param entity
+ * @private
+ */
+ _onDrawStop(entity) {
+ new EditPolyline(entity).start(
+ {
+ viewer: this._viewer,
+ layer: this._layer
+ },
+ {
+ ...this._options,
+ ...{ maxAnchorSize: this._maxAnchorSize }
+ }
+ )
+ }
+
+ /**
+ *
+ * @param positions
+ * @private
+ */
+ _onCalc(positions) {
+ if (positions.length > 0) {
+ this._startLabel.position = positions[0]
+ }
+ if (positions.length > 1) {
+ this._resultLabel.position = positions[1]
+
+ let up = Cesium.Ellipsoid.WGS84.geodeticSurfaceNormal(
+ positions[0],
+ new Cesium.Cartesian3()
+ )
+
+ let plane = Cesium.Plane.fromPointNormal(positions[0], up)
+
+ this._helpLinePositions = [
+ positions[0],
+ Cesium.Plane.projectPointOntoPlane(
+ plane,
+ positions[1],
+ new Cesium.Cartesian3()
+ ),
+ positions[1]
+ ]
+
+ let hegiht = Math.abs(Cesium.Plane.getPointDistance(plane, positions[1]))
+
+ this._resultLabel.label.text = `高度:${hegiht.toFixed(2)} 米`
+ }
+ }
+
+ /**
+ *
+ * @param measure
+ * @param options
+ * @returns {Height}
+ */
+ start(measure, options) {
+ this._startHook(measure, options)
+ let helpLineMaterial = new Cesium.PolylineDashMaterialProperty({
+ color: Cesium.Color.GREENYELLOW
+ })
+ this._helpLine.polyline.material = helpLineMaterial
+ this._helpLine.polyline.depthFailMaterial = helpLineMaterial
+ this._layer.entities.add(this._helpLine)
+ new DrawPolyline({
+ material: options.material || Cesium.Color.YELLOW.withAlpha(0.6),
+ depthFailMaterial:
+ options.depthFailMaterial ||
+ new Cesium.PolylineDashMaterialProperty({
+ color: Cesium.Color.YELLOW.withAlpha(0.6)
+ }),
+ width: options.width || 2
+ }).start(measure, {
+ ...this._options,
+ ...{ maxAnchorSize: this._maxAnchorSize }
+ })
+ return this
+ }
+}
+
+export default Height
diff --git a/src/modules/measure/type/TriangleHeight.js b/src/modules/measure/type/TriangleHeight.js
new file mode 100644
index 00000000..6b42c134
--- /dev/null
+++ b/src/modules/measure/type/TriangleHeight.js
@@ -0,0 +1,169 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-07-17 18:02:35
+ */
+
+import { Cesium } from '@dc-modules/namespace'
+import DrawPolyline from '../draw/DrawPolyline'
+import EditPolyline from '../edit/EditPolyline'
+import MeasureBase from '../MeasureBase'
+
+class TriangleHeight extends MeasureBase {
+ constructor() {
+ super()
+ this._maxAnchorSize = 2
+ this._helpLinePositions = []
+ this._helpLine = new Cesium.Entity({
+ polyline: {
+ positions: new Cesium.CallbackProperty(() => {
+ if (this._helpLinePositions.length > 1) {
+ return this._helpLinePositions
+ } else {
+ return null
+ }
+ }, false)
+ }
+ })
+ this._lengthLabel = new Cesium.Entity({
+ label: {
+ font: '12px',
+ pixelOffset: { x: 0, y: -15 },
+ disableDepthTestDistance: Number.POSITIVE_INFINITY,
+ showBackground: true
+ }
+ })
+ this._heightLabel = new Cesium.Entity({
+ label: {
+ font: '12px',
+ pixelOffset: { x: 0, y: -15 },
+ disableDepthTestDistance: Number.POSITIVE_INFINITY,
+ showBackground: true
+ }
+ })
+ this._distanceLabel = new Cesium.Entity({
+ label: {
+ font: '12px',
+ pixelOffset: { x: 0, y: -15 },
+ disableDepthTestDistance: Number.POSITIVE_INFINITY,
+ showBackground: true
+ }
+ })
+ }
+
+ /**
+ *
+ * @param delegate
+ * @private
+ */
+ _onDrawStop(delegate) {
+ new EditPolyline(delegate).start(
+ {
+ viewer: this._viewer,
+ layer: this._layer
+ },
+ {
+ ...this._options,
+ ...{ maxAnchorSize: this._maxAnchorSize }
+ }
+ )
+ }
+
+ /**
+ *
+ * @param positions
+ * @private
+ */
+ _onCalc(positions) {
+ if (positions.length > 0) {
+ this._startLabel.position = positions[0]
+ }
+ if (positions.length > 1) {
+ let up = Cesium.Ellipsoid.WGS84.geodeticSurfaceNormal(
+ positions[0],
+ new Cesium.Cartesian3()
+ )
+
+ let plane = Cesium.Plane.fromPointNormal(positions[0], up)
+
+ let projectPoint = Cesium.Plane.projectPointOntoPlane(
+ plane,
+ positions[1],
+ new Cesium.Cartesian3()
+ )
+
+ this._helpLinePositions = [positions[0], projectPoint, positions[1]]
+
+ let length = Cesium.Cartesian3.distance(positions[0], projectPoint)
+
+ let hegiht = Math.abs(Cesium.Plane.getPointDistance(plane, positions[1]))
+
+ let distance = Cesium.Cartesian3.distance(positions[0], positions[1])
+
+ this._lengthLabel.position = Cesium.Cartesian3.midpoint(
+ positions[0],
+ projectPoint,
+ new Cesium.Cartesian3()
+ )
+
+ this._lengthLabel.label.text =
+ length > 1000
+ ? `${(length / 1000).toFixed(2)} 公里`
+ : `${length.toFixed(2)} 米`
+
+ this._distanceLabel.position = Cesium.Cartesian3.midpoint(
+ positions[0],
+ positions[1],
+ new Cesium.Cartesian3()
+ )
+ this._distanceLabel.label.text =
+ distance > 1000
+ ? `${(distance / 1000).toFixed(2)} 公里`
+ : `${distance.toFixed(2)} 米`
+
+ this._heightLabel.position = Cesium.Cartesian3.midpoint(
+ projectPoint,
+ positions[1],
+ new Cesium.Cartesian3()
+ )
+ this._heightLabel.label.text =
+ hegiht > 1000
+ ? `${(hegiht / 1000).toFixed(2)} 公里`
+ : `${hegiht.toFixed(2)} 米`
+ }
+ }
+
+ /**
+ *
+ * @param measure
+ * @param options
+ * @returns {TriangleHeight}
+ */
+ start(measure, options) {
+ this._startHook(measure, options)
+ let helpLineMaterial = new Cesium.PolylineDashMaterialProperty({
+ color: Cesium.Color.GREENYELLOW
+ })
+ this._helpLine.polyline.material = helpLineMaterial
+ this._helpLine.polyline.depthFailMaterial = helpLineMaterial
+ this._layer.entities.add(this._helpLine)
+ this._layer.entities.add(this._lengthLabel)
+ this._layer.entities.add(this._heightLabel)
+ this._layer.entities.add(this._distanceLabel)
+ new DrawPolyline({
+ material: options.material || Cesium.Color.YELLOW.withAlpha(0.6),
+ depthFailMaterial:
+ options.depthFailMaterial ||
+ new Cesium.PolylineDashMaterialProperty({
+ color: Cesium.Color.YELLOW.withAlpha(0.6)
+ }),
+ width: options.width || 2,
+ clampToGround: false
+ }).start(measure, {
+ ...options,
+ ...{ maxAnchorSize: this._maxAnchorSize }
+ })
+ return this
+ }
+}
+
+export default TriangleHeight
diff --git a/src/modules/option/CameraOption.js b/src/modules/option/CameraOption.js
new file mode 100644
index 00000000..6aa56a47
--- /dev/null
+++ b/src/modules/option/CameraOption.js
@@ -0,0 +1,127 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-21 15:33:52
+ */
+import { Cesium } from '../../namespace'
+import MouseMode from './MouseMode'
+
+class CameraOption {
+ constructor(viewer) {
+ this._viewer = viewer
+ this._mouseMode = 0
+ }
+
+ /**
+ * @param min
+ * @param max
+ */
+ setPitchRange(min, max) {
+ let handler = new Cesium.ScreenSpaceEventHandler(this._viewer.scene.canvas)
+ if (this._viewer.scene.mode === Cesium.SceneMode.SCENE3D) {
+ handler.setInputAction(
+ (movement) => {
+ handler.setInputAction((movement) => {
+ let enableTilt = true
+ let isUp = movement.endPosition.y < movement.startPosition.y
+ if (
+ isUp &&
+ this._viewer.camera.pitch > Cesium.Math.toRadians(max)
+ ) {
+ enableTilt = false
+ } else if (
+ !isUp &&
+ this._viewer.camera.pitch < Cesium.Math.toRadians(min)
+ ) {
+ enableTilt = false
+ } else {
+ enableTilt = true
+ }
+ this._viewer.scene.screenSpaceCameraController.enableTilt =
+ enableTilt
+ }, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
+ },
+ this._mouseMode === MouseMode.LEFT_MIDDLE
+ ? Cesium.ScreenSpaceEventType.MIDDLE_DOWN
+ : Cesium.ScreenSpaceEventType.RIGHT_DOWN
+ )
+ handler.setInputAction(
+ (movement) => {
+ this._viewer.scene.screenSpaceCameraController.enableTilt = true
+ handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE)
+ },
+ this._mouseMode === MouseMode.LEFT_MIDDLE
+ ? Cesium.ScreenSpaceEventType.MIDDLE_UP
+ : Cesium.ScreenSpaceEventType.RIGHT_UP
+ )
+ }
+ }
+
+ /**
+ *
+ */
+ limitCameraToGround() {
+ this._viewer.camera.changed.addEventListener((frameState) => {
+ if (
+ this._viewer.camera._suspendTerrainAdjustment &&
+ this._viewer.scene.mode === Cesium.SceneMode.SCENE3D
+ ) {
+ this._viewer.camera._suspendTerrainAdjustment = false
+ this._viewer.camera._adjustOrthographicFrustum(true)
+ }
+ })
+ }
+
+ /**
+ * @param west
+ * @param south
+ * @param east
+ * @param north
+ */
+ setBounds(west, south, east, north) {}
+
+ /**
+ *
+ * @param mouseMode
+ */
+ changeMouseMode(mouseMode) {
+ this._mouseMode = mouseMode || MouseMode.LEFT_MIDDLE
+ if (mouseMode === MouseMode.LEFT_MIDDLE) {
+ this._viewer.scene.screenSpaceCameraController.tiltEventTypes = [
+ Cesium.CameraEventType.MIDDLE_DRAG,
+ Cesium.CameraEventType.PINCH,
+ {
+ eventType: Cesium.CameraEventType.LEFT_DRAG,
+ modifier: Cesium.KeyboardEventModifier.CTRL,
+ },
+ {
+ eventType: Cesium.CameraEventType.RIGHT_DRAG,
+ modifier: Cesium.KeyboardEventModifier.CTRL,
+ },
+ ]
+ this._viewer.scene.screenSpaceCameraController.zoomEventTypes = [
+ Cesium.CameraEventType.RIGHT_DRAG,
+ Cesium.CameraEventType.WHEEL,
+ Cesium.CameraEventType.PINCH,
+ ]
+ } else if (mouseMode === MouseMode.LEFT_RIGHT) {
+ this._viewer.scene.screenSpaceCameraController.tiltEventTypes = [
+ Cesium.CameraEventType.RIGHT_DRAG,
+ Cesium.CameraEventType.PINCH,
+ {
+ eventType: Cesium.CameraEventType.LEFT_DRAG,
+ modifier: Cesium.KeyboardEventModifier.CTRL,
+ },
+ {
+ eventType: Cesium.CameraEventType.RIGHT_DRAG,
+ modifier: Cesium.KeyboardEventModifier.CTRL,
+ },
+ ]
+ this._viewer.scene.screenSpaceCameraController.zoomEventTypes = [
+ Cesium.CameraEventType.WHEEL,
+ Cesium.CameraEventType.PINCH,
+ ]
+ }
+ }
+}
+
+export default CameraOption
diff --git a/src/modules/option/MouseMode.js b/src/modules/option/MouseMode.js
new file mode 100644
index 00000000..dd13b0ea
--- /dev/null
+++ b/src/modules/option/MouseMode.js
@@ -0,0 +1,11 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-10-29 20:10:10
+ */
+
+let MouseMode = {
+ LEFT_MIDDLE: 0,
+ LEFT_RIGHT: 1,
+}
+
+export default MouseMode
diff --git a/src/modules/option/ViewerOption.js b/src/modules/option/ViewerOption.js
new file mode 100644
index 00000000..b4105a32
--- /dev/null
+++ b/src/modules/option/ViewerOption.js
@@ -0,0 +1,191 @@
+/**
+ * @Author: Caven
+ * @Date: 2019-12-30 09:24:37
+ */
+
+import { Cesium } from '../../namespace'
+import { Util } from '../utils'
+
+class ViewerOption {
+ constructor(viewer) {
+ this._viewer = viewer
+ this._options = {}
+ }
+
+ /**
+ * Sets viewer option
+ * @returns {ViewerOption}
+ * @private
+ */
+ _setViewerOption() {
+ this._viewer.delegate.shadows = this._options?.shadows ?? false
+ this._viewer.delegate.resolutionScale =
+ this._options?.resolutionScale || 1.0
+ return this
+ }
+
+ /**
+ * sets canvas option
+ * @returns {ViewerOption}
+ * @private
+ */
+ _setCanvasOption() {
+ this._options.tabIndex &&
+ this._viewer.scene.canvas.setAttribute('tabIndex', this._options.tabIndex)
+ return this
+ }
+
+ /**
+ * Sets scene option
+ * @returns {ViewerOption}
+ * @private
+ */
+ _setSceneOption() {
+ let scene = this._viewer.scene
+
+ scene.skyAtmosphere.show = this._options.showAtmosphere ?? true
+
+ scene.sun.show = this._options.showSun ?? true
+
+ scene.moon.show = this._options.showMoon ?? true
+
+ scene.postProcessStages.fxaa.enabled = this._options.enableFxaa ?? false
+
+ if (scene.msaaSupported) {
+ scene.msaaSamples = +this._options.msaaSamples || 1
+ }
+
+ return this
+ }
+
+ /**
+ *
+ * @returns {ViewerOption}
+ * @private
+ */
+ _setSkyBoxOption() {
+ if (!this._options.skyBox) {
+ return this
+ }
+ let skyBoxOption = this._options.skyBox
+ if (skyBoxOption instanceof Cesium.SkyBox) {
+ this._viewer.scene.skyBox = skyBoxOption
+ } else {
+ let skyBox = this._viewer.scene.skyBox
+ skyBox.show = skyBoxOption.show ?? true
+ if (skyBoxOption.offsetAngle) {
+ skyBox.offsetAngle = skyBoxOption.offsetAngle
+ }
+ if (skyBoxOption?.sources) {
+ skyBox.sources = skyBoxOption.sources
+ }
+ }
+ return this
+ }
+
+ /**
+ * Sets globe option
+ * @returns {ViewerOption}
+ * @private
+ */
+ _setGlobeOption() {
+ if (!this._options.globe) {
+ return this
+ }
+
+ let globe = this._viewer.scene.globe
+ let globeOption = this._options.globe
+
+ Util.merge(globe, {
+ show: globeOption?.show ?? true,
+ showGroundAtmosphere: globeOption?.showGroundAtmosphere ?? true,
+ enableLighting: globeOption?.enableLighting ?? false,
+ depthTestAgainstTerrain: globeOption?.depthTestAgainstTerrain ?? false,
+ tileCacheSize: +globeOption?.tileCacheSize || 100,
+ preloadSiblings: globeOption?.enableLighting ?? false,
+ baseColor: globeOption?.baseColor || new Cesium.Color(0, 0, 0.5, 1),
+ terrainExaggeration: globeOption?.terrainExaggeration || 1,
+ terrainExaggerationRelativeHeight:
+ globeOption?.terrainExaggerationRelativeHeight || 0,
+ })
+
+ Util.merge(globe.translucency, {
+ enabled: globeOption?.translucency?.enabled ?? false,
+ backFaceAlpha: +globeOption?.translucency?.backFaceAlpha || 1,
+ backFaceAlphaByDistance:
+ globeOption?.translucency?.backFaceAlphaByDistance,
+ frontFaceAlpha: +globeOption?.translucency?.frontFaceAlpha || 1,
+ frontFaceAlphaByDistance:
+ globeOption?.translucency?.frontFaceAlphaByDistance,
+ })
+
+ // if (globeOption?.filterColor) {
+ // let filterColor = globeOption?.filterColor
+ // let globeFS = globe._surfaceShaderSet.baseFragmentShaderSource
+ // let oriShader = globeFS.sources[globeFS.sources.length - 1]
+ // globeFS.sources[globeFS.sources.length - 1] = oriShader.replace(
+ // 'gl_FragColor = finalColor;',
+ // `gl_FragColor = finalColor * vec4(${filterColor.red.toFixed(
+ // 2
+ // )},${filterColor.green.toFixed(2)},${filterColor.blue.toFixed(
+ // 2
+ // )},${filterColor.alpha.toFixed(2)});`
+ // )
+ // }
+
+ return this
+ }
+
+ /**
+ *
+ * @returns {ViewerOption}
+ * @private
+ */
+ _setCameraController() {
+ if (!this._options?.cameraController) {
+ return this
+ }
+
+ let sscc = this._viewer.scene.screenSpaceCameraController
+ let cameraController = this._options.cameraController
+
+ Util.merge(sscc, {
+ enableInputs: cameraController?.enableInputs ?? true,
+ enableRotate: cameraController?.enableRotate ?? true,
+ enableTilt: cameraController?.enableTilt ?? true,
+ enableTranslate: cameraController?.enableTranslate ?? true,
+ enableZoom: cameraController?.enableZoom ?? true,
+ enableCollisionDetection:
+ cameraController?.enableCollisionDetection ?? true,
+ minimumZoomDistance: +cameraController?.minimumZoomDistance || 1.0,
+ maximumZoomDistance: +cameraController?.maximumZoomDistance || 40489014.0,
+ })
+ return this
+ }
+
+ /**
+ * Sets options
+ * @param options
+ * @returns {ViewerOption}
+ */
+ setOptions(options) {
+ if (Object.keys(options).length === 0) {
+ return this
+ }
+
+ this._options = {
+ ...this._options,
+ ...options,
+ }
+
+ this._setViewerOption()
+ ._setCanvasOption()
+ ._setSceneOption()
+ ._setSkyBoxOption()
+ ._setGlobeOption()
+ ._setCameraController()
+ return this
+ }
+}
+
+export default ViewerOption
diff --git a/src/modules/option/index.js b/src/modules/option/index.js
new file mode 100644
index 00000000..d316e88a
--- /dev/null
+++ b/src/modules/option/index.js
@@ -0,0 +1,8 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-03-05 22:00:17
+ */
+
+export { default as MouseMode } from './MouseMode'
+export { default as ViewerOption } from './ViewerOption'
+export { default as CameraOption } from './CameraOption'
diff --git a/src/modules/overlay/Overlay.js b/src/modules/overlay/Overlay.js
new file mode 100644
index 00000000..1b0be7a2
--- /dev/null
+++ b/src/modules/overlay/Overlay.js
@@ -0,0 +1,340 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-03 12:18:17
+ */
+
+import { Cesium } from '../../namespace'
+import State from '../state/State'
+import { Util } from '../utils'
+import { OverlayEventType, OverlayEvent } from '../event'
+import OverlayType from './OverlayType'
+
+class Overlay {
+ constructor() {
+ this._id = Util.uuid()
+ this._bid = Util.uuid() // Business id
+ this._delegate = undefined
+ this._layer = undefined
+ this._state = undefined
+ this._show = true
+ this._style = {}
+ this._attr = {}
+ this._allowDrillPicking = false
+ this._contextMenu = []
+ this._overlayEvent = new OverlayEvent()
+ this._overlayEvent.on(OverlayEventType.ADD, this._onAdd, this)
+ this._overlayEvent.on(OverlayEventType.REMOVE, this._onRemove, this)
+ }
+
+ get overlayId() {
+ return this._id
+ }
+
+ get type() {
+ return ''
+ }
+
+ set id(id) {
+ this._bid = id
+ return this
+ }
+
+ get id() {
+ return this._bid
+ }
+
+ set show(show) {
+ this._show = show
+ this._delegate && (this._delegate.show = this._show)
+ return this
+ }
+
+ get show() {
+ return this._show
+ }
+
+ set attr(attr) {
+ this._attr = attr
+ return this
+ }
+
+ get attr() {
+ return this._attr
+ }
+
+ set allowDrillPicking(allowDrillPicking) {
+ this._allowDrillPicking = allowDrillPicking
+ return this
+ }
+
+ get allowDrillPicking() {
+ return this._allowDrillPicking
+ }
+
+ get overlayEvent() {
+ return this._overlayEvent
+ }
+
+ get delegate() {
+ return this._delegate
+ }
+
+ get state() {
+ return this._state
+ }
+
+ set contextMenu(menus) {
+ this._contextMenu = menus
+ return this
+ }
+
+ get contextMenu() {
+ return this._contextMenu
+ }
+
+ /**
+ *
+ * @param type
+ * @return {undefined}
+ * @private
+ */
+ _getLayerCollection(type) {
+ let collection = undefined
+ switch (type) {
+ case 'point_primitive':
+ collection = this._layer.points
+ break
+ case 'billboard_primitive':
+ case 'bounce_billboard_primitive':
+ collection = this._layer.billboards
+ break
+ case 'label_primitive':
+ case 'bounce_label_primitive':
+ collection = this._layer.labels
+ break
+ case 'polyline_primitive':
+ collection = this._layer.polylines
+ break
+ case 'cloud_primitive':
+ collection = this._layer.clouds
+ break
+ default:
+ break
+ }
+ return collection
+ }
+
+ /**
+ * The hook for mount layer
+ * Subclasses need to be overridden
+ * @private
+ */
+ _mountedHook() {}
+
+ /**
+ * The hook for added
+ * @returns {boolean}
+ * @private
+ */
+ _addedHook() {
+ if (!this._delegate) {
+ return false
+ }
+ this._delegate.layerId = this._layer?.layerId
+ this._delegate.overlayId = this._id
+ }
+
+ /**
+ * The hook for removed
+ * Subclasses need to be overridden
+ * @private
+ */
+ _removedHook() {}
+
+ /**
+ * Add handler
+ * @param layer
+ * @private
+ */
+ _onAdd(layer) {
+ if (!layer) {
+ return
+ }
+ this._layer = layer
+
+ this._mountedHook && this._mountedHook()
+
+ // for Entity
+ if (this._layer?.delegate?.entities && this._delegate) {
+ this._layer.delegate.entities.add(this._delegate)
+ }
+ // for Primitive
+ else if (this._layer?.delegate?.add) {
+ let collection = this._getLayerCollection(this.type)
+
+ if (collection) {
+ this._delegate && (this._delegate = collection.add(this._delegate))
+ // for custom primitve
+ if (
+ typeof this['update'] === 'function' &&
+ typeof this['destroy'] === 'function'
+ ) {
+ this._layer.delegate.add(this)
+ }
+ } else if (
+ typeof this['update'] === 'function' &&
+ typeof this['destroy'] === 'function'
+ ) {
+ this._layer.delegate.add(this)
+ } else {
+ this._delegate && this._layer.delegate.add(this._delegate)
+ }
+ }
+ this._addedHook && this._addedHook()
+ this._state = State.ADDED
+ }
+
+ /**
+ * Remove handler
+ * @private
+ */
+ _onRemove() {
+ if (!this._layer || !this._delegate) {
+ return
+ }
+ // for Entity
+ if (this._layer?.delegate?.entities) {
+ this._layer.delegate.entities.remove(this._delegate)
+ }
+ // for Primitive
+ else if (this._layer?.delegate?.remove) {
+ let collection = this._getLayerCollection(this.type)
+ if (collection) {
+ this._delegate && collection.remove(this._delegate)
+ // for custom primitve
+ if (
+ typeof this['update'] === 'function' &&
+ typeof this['destroy'] === 'function'
+ ) {
+ this._layer.delegate.remove(this)
+ }
+ } else if (
+ typeof this['update'] === 'function' &&
+ typeof this['destroy'] === 'function'
+ ) {
+ this._layer.delegate.remove(this)
+ } else {
+ this._delegate && this._layer.delegate.remove(this._delegate)
+ }
+ }
+ this._removedHook && this._removedHook()
+ this._state = State.REMOVED
+ }
+
+ /**
+ * Sets Text with Style
+ * @param text
+ * @param textStyle
+ * @returns {Overlay}
+ */
+ setLabel(text, textStyle) {
+ if (!this._delegate) {
+ return this
+ }
+ if (this._delegate instanceof Cesium.Entity) {
+ this._delegate.label = {
+ ...textStyle,
+ text: text,
+ }
+ }
+ return this
+ }
+
+ /**
+ * Sets style
+ * @param style
+ * @returns {Overlay}
+ */
+ setStyle(style) {
+ return this
+ }
+
+ /**
+ * Removes from layer
+ * @returns {Overlay}
+ */
+ remove() {
+ if (this._layer) {
+ this._layer.removeOverlay(this)
+ }
+ return this
+ }
+
+ /**
+ * adds to layer
+ * @param layer
+ * @returns {Overlay}
+ */
+ addTo(layer) {
+ if (layer && layer.addOverlay) {
+ layer.addOverlay(this)
+ }
+ return this
+ }
+
+ /**
+ * Subscribe event
+ * @param type
+ * @param callback
+ * @param context
+ * @returns {Overlay}
+ */
+ on(type, callback, context) {
+ this._overlayEvent.on(type, callback, context || this)
+ return this
+ }
+
+ /**
+ * Unsubscribe event
+ * @param type
+ * @param callback
+ * @param context
+ * @returns {Overlay}
+ */
+ off(type, callback, context) {
+ this._overlayEvent.off(type, callback, context || this)
+ return this
+ }
+
+ /**
+ * Trigger subscription event
+ * @param type
+ * @param params
+ * @returns {Overlay}
+ */
+ fire(type, params) {
+ this._overlayEvent.fire(type, params)
+ return this
+ }
+
+ /**
+ *
+ * @param type
+ */
+ static registerType(type) {
+ if (type) {
+ OverlayType[type.toLocaleUpperCase()] = type.toLocaleLowerCase()
+ }
+ }
+
+ /**
+ *
+ * @param type
+ * @returns {*|undefined}
+ */
+ static getOverlayType(type) {
+ return OverlayType[type.toLocaleUpperCase()] || undefined
+ }
+}
+
+export default Overlay
diff --git a/src/modules/overlay/OverlayType.js b/src/modules/overlay/OverlayType.js
new file mode 100644
index 00000000..577b25e1
--- /dev/null
+++ b/src/modules/overlay/OverlayType.js
@@ -0,0 +1,8 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-15 11:57:02
+ */
+
+let OverlayType = {}
+
+export default OverlayType
diff --git a/src/modules/overlay/custom/CustomBillboard.js b/src/modules/overlay/custom/CustomBillboard.js
new file mode 100644
index 00000000..708412b4
--- /dev/null
+++ b/src/modules/overlay/custom/CustomBillboard.js
@@ -0,0 +1,146 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-02-12 21:44:24
+ */
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import Parse from '../../parse/Parse'
+import State from '../../state/State'
+import { Transform } from '../../transform'
+import { Util } from '../../utils'
+
+class CustomBillboard extends Overlay {
+ constructor(position, icon) {
+ super()
+ this._delegate = new Cesium.Entity({ billboard: {} })
+ this._position = Parse.parsePosition(position)
+ this._icon = icon
+ this._size = [32, 32]
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('custom_billboard')
+ }
+
+ set position(position) {
+ this._position = Parse.parsePosition(position)
+ this._delegate.position = Transform.transformWGS84ToCartesian(
+ this._position
+ )
+ }
+
+ get position() {
+ return this._position
+ }
+
+ set icon(icon) {
+ this._icon = icon
+ this._delegate.billboard.image = this._icon
+ }
+
+ get icon() {
+ return this._icon
+ }
+
+ set size(size) {
+ if (!Array.isArray(size)) {
+ throw new Error('CustomBillboard: the size invalid')
+ }
+ this._size = size
+ this._delegate.billboard.width = this._size[0] || 32
+ this._delegate.billboard.height = this._size[1] || 32
+ }
+
+ get size() {
+ return this._size
+ }
+
+ _mountedHook() {
+ /**
+ * set the location
+ */
+ this.position = this._position
+ /**
+ * initialize the Overlay parameter
+ */
+ this.icon = this._icon
+ this.size = this._size
+ }
+
+ /**
+ * Sets label
+ * @param text
+ * @param textStyle
+ * @returns {CustomBillboard}
+ */
+ setLabel(text, textStyle) {
+ this._delegate.label = {
+ ...textStyle,
+ text: text,
+ }
+ return this
+ }
+
+ /**
+ * Sets Style
+ * @param style
+ * @returns {CustomBillboard}
+ */
+ setStyle(style) {
+ if (!style || Object.keys(style).length === 0) {
+ return this
+ }
+ delete style['image'] && delete style['width'] && delete style['height']
+ Util.merge(this._style, style)
+ Util.merge(this._delegate.billboard, style)
+ return this
+ }
+
+ /**
+ * Sets VLine style
+ * @param style
+ * @returns {CustomBillboard}
+ */
+ setVLine(style = {}) {
+ if (this._position.alt > 0 && !this._delegate.polyline) {
+ let position = this._position.copy()
+ position.alt = style.height || 0
+ this._delegate.polyline = {
+ ...style,
+ positions: Transform.transformWGS84ArrayToCartesianArray([
+ position,
+ this._position,
+ ]),
+ }
+ }
+ return this
+ }
+
+ /**
+ * @param {*} radius
+ * @param {*} style
+ * @param {*} rotateAmount
+ */
+ setBottomCircle(radius, style = {}, rotateAmount = 0) {
+ let stRotation = 0
+ let amount = rotateAmount
+ this._delegate.ellipse = {
+ ...style,
+ semiMajorAxis: radius,
+ semiMinorAxis: radius,
+ stRotation: new Cesium.CallbackProperty(() => {
+ stRotation += amount
+ if (stRotation >= 360 || stRotation <= -360) {
+ stRotation = 0
+ }
+ return stRotation
+ }, false),
+ }
+ return this
+ }
+}
+
+Overlay.registerType('custom_billboard')
+
+export default CustomBillboard
diff --git a/src/modules/overlay/custom/CustomLabel.js b/src/modules/overlay/custom/CustomLabel.js
new file mode 100644
index 00000000..d2d04811
--- /dev/null
+++ b/src/modules/overlay/custom/CustomLabel.js
@@ -0,0 +1,118 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-07-28 18:37:59
+ */
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import Parse from '../../parse/Parse'
+import State from '../../state/State'
+import { Transform } from '../../transform'
+import { Util } from '../../utils'
+
+class CustomLabel extends Overlay {
+ constructor(position, text) {
+ super()
+ this._delegate = new Cesium.Entity({ label: {} })
+ this._position = Parse.parsePosition(position)
+ this._text = text
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('custom_label')
+ }
+
+ set position(position) {
+ this._position = Parse.parsePosition(position)
+ this._delegate.position = Transform.transformWGS84ToCartesian(
+ this._position
+ )
+ }
+
+ get position() {
+ return this._position
+ }
+
+ set text(text) {
+ this._text = text
+ this._delegate.label.text = this._text
+ }
+
+ get text() {
+ return this._text
+ }
+
+ _mountedHook() {
+ /**
+ * set the location
+ */
+ this.position = this._position
+ /**
+ * initialize the Overlay parameter
+ */
+ this.text = this._text
+ }
+
+ /**
+ *
+ * @param {*} style
+ */
+ setStyle(style) {
+ if (!style || Object.keys(style).length === 0) {
+ return this
+ }
+ delete style['text']
+ Util.merge(this._style, style)
+ Util.merge(this._delegate.label, style)
+ return this
+ }
+
+ /**
+ * Sets VLine style
+ * @param style
+ * @returns {CustomLabel}
+ */
+ setVLine(style = {}) {
+ if (this._position.alt > 0 && !this._delegate.polyline) {
+ let position = this._position.copy()
+ position.alt = style.height || 0
+ this._delegate.polyline = {
+ ...style,
+ positions: Transform.transformWGS84ArrayToCartesianArray([
+ position,
+ this._position,
+ ]),
+ }
+ }
+ return this
+ }
+
+ /**
+ * Sets bottom circle
+ * @param radius
+ * @param style
+ * @param rotateAmount
+ * @returns {CustomLabel}
+ */
+ setBottomCircle(radius, style = {}, rotateAmount = 0) {
+ let stRotation = 0
+ let amount = rotateAmount
+ this._delegate.ellipse = {
+ ...style,
+ semiMajorAxis: radius,
+ semiMinorAxis: radius,
+ stRotation: new Cesium.CallbackProperty(() => {
+ stRotation += amount
+ if (stRotation >= 360 || stRotation <= -360) {
+ stRotation = 0
+ }
+ return stRotation
+ }, false),
+ }
+ return this
+ }
+}
+
+Overlay.registerType('custom_label')
+
+export default CustomLabel
diff --git a/src/modules/overlay/dynamic/DynamicBillboard.js b/src/modules/overlay/dynamic/DynamicBillboard.js
new file mode 100644
index 00000000..4e3eba06
--- /dev/null
+++ b/src/modules/overlay/dynamic/DynamicBillboard.js
@@ -0,0 +1,89 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-05-05 09:16:35
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import DynamicOverlay from './DynamicOverlay'
+import Parse from '../../parse/Parse'
+import State from '../../state/State'
+import { Transform } from '../../transform'
+import { Util } from '../../utils'
+
+class DynamicBillboard extends DynamicOverlay {
+ constructor(position, icon) {
+ super()
+ this._posistion = Parse.parsePosition(position)
+ this._icon = icon
+ this._delegate = new Cesium.Entity({ billboard: {} })
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('dynamic_billboard')
+ }
+
+ set icon(icon) {
+ this._icon = icon
+ this._delegate.billboard.image = this._icon
+ return this
+ }
+
+ get icon() {
+ return this._icon
+ }
+
+ set size(size) {
+ if (!Array.isArray(size)) {
+ throw new Error('DynamicBillboard: the size invalid')
+ }
+ this._size = size
+ this._delegate.billboard.width = this._size[0] || 32
+ this._delegate.billboard.height = this._size[1] || 32
+ return this
+ }
+
+ get size() {
+ return this._size
+ }
+
+ _mountedHook() {
+ /**
+ * set the location
+ */
+ this._sampledPosition.forwardExtrapolationType =
+ Cesium.ExtrapolationType.HOLD
+ this._startTime = Cesium.JulianDate.now()
+ this._sampledPosition.addSample(
+ this._startTime,
+ Transform.transformWGS84ToCartesian(this._posistion)
+ )
+ this._delegate.position = this._sampledPosition
+ this._cache.push(this._startTime)
+ /**
+ * initialize the Overlay parameter
+ */
+ this.icon = this._icon
+ this.size = this._size
+ }
+
+ /**
+ *
+ * @param style
+ * @returns {DynamicBillboard}
+ */
+ setStyle(style) {
+ if (!style || Object.keys(style).length === 0) {
+ return this
+ }
+ delete style['image'] && delete style['width'] && delete style['height']
+ Util.merge(this._style, style)
+ Util.merge(this._delegate.billboard, style)
+ return this
+ }
+}
+
+Overlay.registerType('dynamic_billboard')
+
+export default DynamicBillboard
diff --git a/src/modules/overlay/dynamic/DynamicModel.js b/src/modules/overlay/dynamic/DynamicModel.js
new file mode 100644
index 00000000..b342a6dd
--- /dev/null
+++ b/src/modules/overlay/dynamic/DynamicModel.js
@@ -0,0 +1,76 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-05-05 09:16:35
+ */
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import DynamicOverlay from './DynamicOverlay'
+import Parse from '../../parse/Parse'
+import State from '../../state/State'
+import { Transform } from '../../transform'
+import { Util } from '../../utils'
+
+class DynamicModel extends DynamicOverlay {
+ constructor(position, modelUrl) {
+ super()
+ this._posistion = Parse.parsePosition(position)
+ this._modelUrl = modelUrl
+ this._delegate = new Cesium.Entity({ model: {} })
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('dynamic_model')
+ }
+
+ set modelUrl(modelUrl) {
+ this._modelUrl = modelUrl
+ this._delegate.model.uri = this._modelUrl
+ return this
+ }
+
+ get modelUrl() {
+ return this._modelUrl
+ }
+
+ _mountedHook() {
+ /**
+ * set the location
+ */
+ this._sampledPosition.forwardExtrapolationType =
+ Cesium.ExtrapolationType.HOLD
+ this._startTime = Cesium.JulianDate.now()
+ this._sampledPosition.addSample(
+ this._startTime,
+ Transform.transformWGS84ToCartesian(this._posistion)
+ )
+ this._delegate.position = this._sampledPosition
+ this._delegate.orientation = new Cesium.VelocityOrientationProperty(
+ this._sampledPosition
+ )
+ this._cache.push(this._startTime)
+ /**
+ * initialize the Overlay parameter
+ */
+ this.modelUrl = this._modelUrl
+ }
+
+ /**
+ * Sets style
+ * @param style
+ * @returns {DynamicModel}
+ */
+ setStyle(style) {
+ if (!style || Object.keys(style).length === 0) {
+ return this
+ }
+ delete style['uri']
+ Util.merge(this._style, style)
+ Util.merge(this._delegate.model, style)
+ return this
+ }
+}
+
+Overlay.registerType('dynamic_model')
+
+export default DynamicModel
diff --git a/src/modules/overlay/dynamic/DynamicOverlay.js b/src/modules/overlay/dynamic/DynamicOverlay.js
new file mode 100644
index 00000000..52650b00
--- /dev/null
+++ b/src/modules/overlay/dynamic/DynamicOverlay.js
@@ -0,0 +1,97 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-05-05 09:16:35
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import Parse from '../../parse/Parse'
+import State from '../../state/State'
+import { Transform } from '../../transform'
+
+class DynamicOverlay extends Overlay {
+ constructor() {
+ super()
+ this._startTime = undefined
+ this._lastTime = undefined
+ this._sampledPosition = new Cesium.SampledPositionProperty()
+ this._cache = []
+ this._maxCacheSize = 10
+ this._state = State.INITIALIZED
+ }
+
+ set maxCacheSize(maxCacheSize) {
+ this._maxCacheSize = maxCacheSize
+ return this
+ }
+
+ get maxCacheSize() {
+ return this._maxCacheSize
+ }
+
+ get position() {
+ return Transform.transformCartesianToWGS84(
+ this._sampledPosition.getValue(Cesium.JulianDate.now())
+ )
+ }
+
+ /**
+ *
+ * @private
+ */
+ _removePosition() {
+ if (this._cache.length > this._maxCacheSize) {
+ let start = Cesium.JulianDate.addSeconds(
+ this._cache[0],
+ -0.2,
+ new Cesium.JulianDate()
+ )
+ let stop = Cesium.JulianDate.addSeconds(
+ this._cache[this._cache.length - this._maxCacheSize],
+ -0.2,
+ new Cesium.JulianDate()
+ )
+ this._sampledPosition.removeSamples(
+ new Cesium.TimeInterval({
+ start: start,
+ stop: stop,
+ })
+ )
+ this._cache.splice(0, this._cache.length - this._maxCacheSize)
+ }
+ }
+
+ /**
+ *
+ * @param position
+ * @param interval
+ * @returns {DynamicOverlay}
+ */
+ addPosition(position, interval) {
+ this._removePosition()
+ let now = Cesium.JulianDate.now()
+ let time = Cesium.JulianDate.addSeconds(
+ now,
+ interval,
+ new Cesium.JulianDate()
+ )
+ this._sampledPosition.addSample(
+ time,
+ Transform.transformWGS84ToCartesian(Parse.parsePosition(position))
+ )
+ this._lastTime = time
+ this._cache.push(this._lastTime)
+ return this
+ }
+
+ /**
+ *
+ * @param content
+ * @returns {DynamicOverlay}
+ */
+ bindDom(content) {
+ return this
+ }
+}
+
+export default DynamicOverlay
diff --git a/src/modules/overlay/html/DivIcon.js b/src/modules/overlay/html/DivIcon.js
new file mode 100644
index 00000000..05531678
--- /dev/null
+++ b/src/modules/overlay/html/DivIcon.js
@@ -0,0 +1,223 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-02-12 21:46:22
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import Parse from '../../parse/Parse'
+import State from '../../state/State'
+import { Transform } from '../../transform'
+import { isBetween } from '../../math'
+import { Util, DomUtil } from '../../utils'
+import { MouseEventType } from '../../event'
+
+class DivIcon extends Overlay {
+ constructor(position, content) {
+ super()
+ this._delegate = DomUtil.create('div', 'div-icon')
+ this._position = Parse.parsePosition(position)
+ this._delegate.setAttribute('id', this._id)
+ Util.merge(this._delegate.style, {
+ position: 'absolute',
+ top: '0',
+ left: '0',
+ })
+ this.content = content
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('div_icon')
+ }
+
+ set show(show) {
+ this._show = show
+ this._delegate.style.visibility = this._show ? 'visible' : 'hidden'
+ }
+
+ get show() {
+ return this._show
+ }
+
+ set position(position) {
+ this._position = Parse.parsePosition(position)
+ }
+
+ get position() {
+ return this._position
+ }
+
+ set content(content) {
+ if (content && typeof content === 'string') {
+ this._delegate.innerHTML = content
+ } else if (content && content instanceof Element) {
+ while (this._delegate.hasChildNodes()) {
+ this._delegate.removeChild(this._delegate.firstChild)
+ }
+ this._delegate.appendChild(content)
+ }
+ }
+
+ get content() {
+ return this._delegate.childNodes || []
+ }
+
+ /**
+ * Updates style
+ * @param windowCoord
+ * @param distance
+ * @param isFront
+ * @private
+ */
+ _updateStyle(windowCoord, distance, isFront) {
+ if (!this._show || !windowCoord) {
+ return
+ }
+
+ // set translate
+ let x = windowCoord.x - this._delegate.offsetWidth / 2
+ let y = windowCoord.y - this._delegate.offsetHeight / 2
+
+ if (this._style.position === 'topleft') {
+ x = windowCoord.x - this._delegate.offsetWidth
+ y = windowCoord.y - this._delegate.offsetHeight
+ } else if (this._style.position === 'topright') {
+ x = windowCoord.x
+ y = windowCoord.y - this._delegate.offsetHeight
+ } else if (this._style.position === 'bottomleft') {
+ x = windowCoord.x - this._delegate.offsetWidth
+ y = windowCoord.y
+ } else if (this._style.position === 'bottomright') {
+ x = windowCoord.x
+ y = windowCoord.y
+ }
+ let translate3d = `translate3d(${Math.round(x)}px,${Math.round(y)}px, 0)`
+
+ // set scale
+ let scale3d = 'scale3d(1,1,1)'
+ let scaleByDistance = this._style.scaleByDistance
+ if (distance && scaleByDistance) {
+ let near = scaleByDistance.near || 0.0
+ let nearValue = scaleByDistance.nearValue || 1.0
+ let far = scaleByDistance.far || Number.MAX_VALUE
+ let farValue = scaleByDistance.farValue || 0.0
+ let f = distance / far
+ if (distance < near) {
+ scale3d = `scale3d(${nearValue},${nearValue},1)`
+ } else if (distance > far) {
+ scale3d = `scale3d(${farValue},${farValue},1)`
+ } else {
+ let scale = farValue + f * (nearValue - farValue)
+ scale3d = `scale3d(${scale},${scale},1)`
+ }
+ }
+
+ // set condition
+ let isDisplay = true
+ let distanceDisplayCondition = this._style.distanceDisplayCondition
+ if (distance && distanceDisplayCondition) {
+ isDisplay = isBetween(
+ distance,
+ distanceDisplayCondition.near || 0.0,
+ distanceDisplayCondition.far || Number.MAX_VALUE
+ )
+ }
+
+ // update style
+ this._delegate.style.transform = `${translate3d} ${scale3d}`
+ this._delegate.style.visibility =
+ isDisplay && isFront ? 'visible' : 'hidden'
+ }
+
+ /**
+ *
+ * @param layer
+ * @returns {boolean}
+ * @private
+ */
+ _onAdd(layer) {
+ this._layer = layer
+ this._layer.delegate.appendChild(this._delegate)
+ let params = {
+ layer: layer,
+ overlay: this,
+ position: Transform.transformWGS84ToCartesian(this._position),
+ }
+
+ this._delegate.addEventListener('click', () => {
+ this._overlayEvent.fire(MouseEventType.CLICK, params)
+ })
+
+ this._delegate.addEventListener('mouseover', () => {
+ this._overlayEvent.fire(MouseEventType.MOUSE_OVER, params)
+ })
+
+ this._delegate.addEventListener('mouseout', () => {
+ this._overlayEvent.fire(MouseEventType.MOUSE_OUT, params)
+ })
+
+ this._state = State.ADDED
+ }
+
+ /**
+ *
+ * @private
+ */
+ _onRemove() {
+ if (this._layer) {
+ this._layer.delegate.removeChild(this._delegate)
+ this._state = State.REMOVED
+ }
+ }
+
+ /**
+ * Sets text
+ * @param text
+ * @param textStyle
+ * @returns {DivIcon}
+ */
+ setLabel(text, textStyle) {
+ return this
+ }
+
+ /**
+ * Sets style
+ * @param style
+ * @returns {DivIcon}
+ */
+ setStyle(style) {
+ if (!style || Object.keys(style).length === 0) {
+ return this
+ }
+ Util.merge(this._style, style)
+ this._style.className &&
+ DomUtil.addClass(this._delegate, this._style.className)
+ return this
+ }
+
+ /**
+ * Parse from entity
+ * @param entity
+ * @param content
+ * @returns {DivIcon}
+ */
+ static fromEntity(entity, content) {
+ let divIcon
+ let now = Cesium.JulianDate.now()
+ let position = Transform.transformCartesianToWGS84(
+ entity.position.getValue(now)
+ )
+ divIcon = new DivIcon(position, content)
+ if (entity.billboard) {
+ divIcon.attr = {
+ ...entity?.properties?.getValue(now),
+ }
+ }
+ return divIcon
+ }
+}
+
+Overlay.registerType('div_icon')
+
+export default DivIcon
diff --git a/src/modules/overlay/index.js b/src/modules/overlay/index.js
new file mode 100644
index 00000000..fb49c6b1
--- /dev/null
+++ b/src/modules/overlay/index.js
@@ -0,0 +1,79 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-04-15 20:57:22
+ */
+
+export { default as OverlayType } from './OverlayType'
+export { default as Overlay } from './Overlay'
+
+/**
+ * custom
+ */
+export { default as CustomBillboard } from './custom/CustomBillboard'
+export { default as CustomLabel } from './custom/CustomLabel'
+
+/**
+ * dynamic
+ */
+export { default as DynamicBillboard } from './dynamic/DynamicBillboard'
+export { default as DynamicModel } from './dynamic/DynamicModel'
+
+/**
+ * html
+ */
+export { default as DivIcon } from './html/DivIcon'
+
+/**
+ * model
+ */
+export { default as Model } from './model/Model'
+export { default as Tileset } from './model/Tileset'
+
+/**
+ * plot
+ */
+export { default as AttackArrow } from './plot/AttackArrow'
+export { default as DoubleArrow } from './plot/DoubleArrow'
+export { default as FineArrow } from './plot/FineArrow'
+export { default as GatheringPlace } from './plot/GatheringPlace'
+export { default as TailedAttackArrow } from './plot/TailedAttackArrow'
+
+/**
+ * primitive
+ */
+export { default as BillboardPrimitive } from './primitive/BillboardPrimitive.js'
+export { default as BounceBillboardPrimitive } from './primitive/BounceBillboardPrimitive'
+export { default as BounceLabelPrimitive } from './primitive/BounceLabelPrimitive'
+export { default as CloudPrimitive } from './primitive/CloudPrimitive'
+export { default as DiffuseWallPrimitive } from './primitive/DiffuseWallPrimitive.js'
+export { default as ElecEllipsoidPrimitive } from './primitive/ElecEllipsoidPrimitive'
+export { default as FlowLinePrimitive } from './primitive/FlowLinePrimitive'
+export { default as LabelPrimitive } from './primitive/LabelPrimitive'
+export { default as LightCylinderPrimitive } from './primitive/LightCylinderPrimitive'
+// export { default as ModelCollectionPrimitive } from './primitive/ModelCollectionPrimitive'
+export { default as ModelPrimitive } from './primitive/ModelPrimitive'
+export { default as PointPrimitive } from './primitive/PointPrimitive.js'
+export { default as PolylinePrimitive } from './primitive/PolylinePrimitive.js'
+export { default as ScanCirclePrimitive } from './primitive/ScanCirclePrimitive'
+export { default as TrailLinePrimitive } from './primitive/TrailLinePrimitive'
+export { default as VideoPrimitive } from './primitive/VideoPrimitive'
+export { default as WaterPrimitive } from './primitive/WaterPrimitive'
+
+/**
+ * vector
+ */
+export { default as Billboard } from './vector/Billboard'
+export { default as Box } from './vector/Box'
+export { default as Circle } from './vector/Circle'
+export { default as Corridor } from './vector/Corridor'
+export { default as Cylinder } from './vector/Cylinder'
+export { default as Ellipse } from './vector/Ellipse'
+export { default as Ellipsoid } from './vector/Ellipsoid'
+export { default as Label } from './vector/Label'
+export { default as Plane } from './vector/Plane'
+export { default as Point } from './vector/Point'
+export { default as Polygon } from './vector/Polygon'
+export { default as Polyline } from './vector/Polyline'
+export { default as PolylineVolume } from './vector/PolylineVolume'
+export { default as Rectangle } from './vector/Rectangle'
+export { default as Wall } from './vector/Wall'
diff --git a/src/modules/overlay/model/Model.js b/src/modules/overlay/model/Model.js
new file mode 100644
index 00000000..aa23be6a
--- /dev/null
+++ b/src/modules/overlay/model/Model.js
@@ -0,0 +1,126 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-06 20:03:25
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import Parse from '../../parse/Parse'
+import State from '../../state/State'
+import { Transform } from '../../transform'
+import { Util } from '../../utils'
+
+class Model extends Overlay {
+ constructor(position, modelUrl) {
+ super()
+ this._delegate = new Cesium.Entity({ model: {} })
+ this._position = Parse.parsePosition(position)
+ this._modelUrl = modelUrl
+ this._rotateAmount = 0
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('model')
+ }
+
+ set position(position) {
+ this._position = Parse.parsePosition(position)
+ this._delegate.position = Transform.transformWGS84ToCartesian(
+ this._position
+ )
+ if (this._rotateAmount === 0) {
+ this._delegate.orientation = Cesium.Transforms.headingPitchRollQuaternion(
+ Transform.transformWGS84ToCartesian(this._position),
+ new Cesium.HeadingPitchRoll(
+ Cesium.Math.toRadians(this._position.heading),
+ Cesium.Math.toRadians(this._position.pitch),
+ Cesium.Math.toRadians(this._position.roll)
+ )
+ )
+ }
+ }
+
+ get position() {
+ return this._position
+ }
+
+ set modelUrl(modelUrl) {
+ this._modelUrl = modelUrl
+ this._delegate.model.uri = this._modelUrl
+ }
+
+ get modelUrl() {
+ return this._modelUrl
+ }
+
+ set rotateAmount(amount) {
+ this._rotateAmount = +amount
+ this._delegate.orientation = new Cesium.CallbackProperty(() => {
+ this._position.heading += this._rotateAmount
+ if (this._position.heading >= 360 || this._position.heading <= -360) {
+ this._position.heading = 0
+ }
+ return Cesium.Transforms.headingPitchRollQuaternion(
+ Transform.transformWGS84ToCartesian(this._position),
+ new Cesium.HeadingPitchRoll(
+ Cesium.Math.toRadians(this._position.heading),
+ Cesium.Math.toRadians(this._position.pitch),
+ Cesium.Math.toRadians(this._position.roll)
+ )
+ )
+ }, false)
+ }
+
+ get rotateAmount() {
+ return this._rotateAmount
+ }
+
+ _mountedHook() {
+ /**
+ * set the location
+ */
+ this.position = this._position
+ /**
+ * initialize the Overlay parameter
+ */
+ this.modelUrl = this._modelUrl
+ }
+
+ /**
+ * Sets style
+ * @param style
+ * @returns {Model}
+ */
+ setStyle(style) {
+ if (!style || Object.keys(style).length === 0) {
+ return this
+ }
+ delete style['uri']
+ Util.merge(this._style, style)
+ Util.merge(this._delegate.model, style)
+ return this
+ }
+
+ /**
+ * Parse from entity
+ * @param entity
+ * @param modelUrl
+ * @returns {Model}
+ */
+ static fromEntity(entity, modelUrl) {
+ let now = Cesium.JulianDate.now()
+ let position = Transform.transformCartesianToWGS84(
+ entity.position.getValue(now)
+ )
+ let model = new Model(position, modelUrl)
+ model.attr = {
+ ...entity.properties.getValue(now),
+ }
+ return model
+ }
+}
+
+Overlay.registerType('model')
+
+export default Model
diff --git a/src/modules/overlay/model/Tileset.js b/src/modules/overlay/model/Tileset.js
new file mode 100644
index 00000000..936134f3
--- /dev/null
+++ b/src/modules/overlay/model/Tileset.js
@@ -0,0 +1,256 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-07 20:51:56
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import Parse from '../../parse/Parse'
+import State from '../../state/State'
+
+class Tileset extends Overlay {
+ constructor(url, options = {}) {
+ super()
+ this._delegate = new Cesium.Cesium3DTileset({
+ ...options,
+ url: url,
+ })
+ this._tileVisibleCallback = undefined
+ this._properties = undefined
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('tileset')
+ }
+
+ get readyPromise() {
+ return this._delegate.readyPromise
+ }
+
+ /**
+ *
+ * @private
+ */
+ _bindVisibleEvent() {
+ this._tileVisibleCallback && this._tileVisibleCallback()
+ this._tileVisibleCallback = this._delegate.tileVisible.addEventListener(
+ this._updateTile,
+ this
+ )
+ }
+
+ /**
+ * Updates tile
+ * @param tile
+ * @private
+ */
+ _updateTile(tile) {
+ let content = tile.content
+ // sets properties
+ for (let i = 0; i < content.featuresLength; i++) {
+ let feature = content.getFeature(i)
+ if (this._properties && this._properties.length) {
+ this._properties.forEach((property) => {
+ if (
+ feature.hasProperty(property['key']) &&
+ feature.getProperty(property['key']) === property['keyValue']
+ ) {
+ feature.setProperty(
+ property['propertyName'],
+ property['propertyValue']
+ )
+ }
+ })
+ }
+ }
+ }
+
+ /**
+ * Sets position
+ * @param position
+ * @returns {Tileset}
+ */
+ setPosition(position) {
+ position = Parse.parsePosition(position)
+ this.readyPromise.then((tileset) => {
+ let modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(
+ Cesium.Cartesian3.fromDegrees(position.lng, position.lat, position.alt)
+ )
+ let rotation = Cesium.Matrix4.fromRotationTranslation(
+ Cesium.Matrix3.fromHeadingPitchRoll(
+ new Cesium.HeadingPitchRoll(
+ Cesium.Math.toRadians(position.heading),
+ Cesium.Math.toRadians(position.pitch),
+ Cesium.Math.toRadians(position.roll)
+ )
+ )
+ )
+ Cesium.Matrix4.multiply(modelMatrix, rotation, modelMatrix)
+ tileset.root.transform = modelMatrix
+ })
+ return this
+ }
+
+ /**
+ *
+ * @param heading
+ * @param pitch
+ * @param roll
+ * @returns {Tileset}
+ */
+ setHeadingPitchRoll(heading, pitch, roll) {
+ this.readyPromise.then((tileset) => {
+ let modelMatrix = tileset.root.transform
+ let rotation = Cesium.Matrix4.fromRotationTranslation(
+ Cesium.Matrix3.fromHeadingPitchRoll(
+ new Cesium.HeadingPitchRoll(
+ Cesium.Math.toRadians(heading || 0),
+ Cesium.Math.toRadians(pitch || 0),
+ Cesium.Math.toRadians(roll || 0)
+ )
+ )
+ )
+ Cesium.Matrix4.multiply(modelMatrix, rotation, modelMatrix)
+ tileset.root.transform = modelMatrix
+ })
+ return this
+ }
+
+ /**
+ *
+ * @param {*} text
+ * @param {*} textStyle
+ */
+ setLabel(text, textStyle) {
+ return this
+ }
+
+ /**
+ * Clamps To Ground
+ * @returns {Tileset}
+ */
+ clampToGround() {
+ this.readyPromise.then((tileset) => {
+ let center = Cesium.Cartographic.fromCartesian(
+ tileset.boundingSphere.center
+ )
+ let surface = Cesium.Cartesian3.fromRadians(
+ center.longitude,
+ center.latitude,
+ center.height
+ )
+ let offset = Cesium.Cartesian3.fromRadians(
+ center.longitude,
+ center.latitude,
+ 0
+ )
+ let translation = Cesium.Cartesian3.subtract(
+ offset,
+ surface,
+ new Cesium.Cartesian3()
+ )
+ tileset.modelMatrix = Cesium.Matrix4.fromTranslation(translation)
+ })
+ return this
+ }
+
+ /**
+ * Sets height
+ * @param height
+ * @param isAbsolute
+ * @returns {Tileset}
+ */
+ setHeight(height, isAbsolute = false) {
+ this.readyPromise.then((tileset) => {
+ let center = Cesium.Cartographic.fromCartesian(
+ tileset.boundingSphere.center
+ )
+ let surface = Cesium.Cartesian3.fromRadians(
+ center.longitude,
+ center.latitude,
+ center.height
+ )
+ let offset = Cesium.Cartesian3.fromRadians(
+ center.longitude,
+ center.latitude,
+ isAbsolute ? height : center.height + height
+ )
+ let translation = Cesium.Cartesian3.subtract(
+ offset,
+ surface,
+ new Cesium.Cartesian3()
+ )
+ tileset.modelMatrix = Cesium.Matrix4.fromTranslation(translation)
+ })
+ return this
+ }
+
+ /**
+ * Sets scale
+ * @param scale
+ * @returns {Tileset}
+ */
+ setScale(scale) {
+ this.readyPromise.then((tileset) => {
+ let modelMatrix = tileset.root.transform
+ if (scale > 0 && scale !== 1) {
+ Cesium.Matrix4.multiplyByUniformScale(modelMatrix, scale, modelMatrix)
+ }
+ tileset.root.transform = modelMatrix
+ })
+ return this
+ }
+
+ /**
+ * Sets feature property
+ * @param properties
+ * @returns {Tileset}
+ */
+ setProperties(properties) {
+ this._properties = properties
+ this._bindVisibleEvent()
+ return this
+ }
+
+ /**
+ *
+ * @param splitDirection
+ * @return {Tileset}
+ */
+ setSplitDirection(splitDirection) {
+ this.readyPromise.then((tileset) => {
+ tileset.splitDirection = splitDirection
+ })
+ return this
+ }
+
+ /**
+ *
+ * @param customShader
+ * @return {Tileset}
+ */
+ setCustomShader(customShader) {
+ this.readyPromise.then((tileset) => {
+ tileset.customShader = customShader
+ })
+ return this
+ }
+
+ /**
+ * Sets style
+ * @param style
+ * @returns {Tileset}
+ */
+ setStyle(style) {
+ if (style && style instanceof Cesium.Cesium3DTileStyle) {
+ this._style = style
+ this._delegate.style = this._style
+ }
+ return this
+ }
+}
+
+Overlay.registerType('tileset')
+
+export default Tileset
diff --git a/src/modules/overlay/plot/AttackArrow.js b/src/modules/overlay/plot/AttackArrow.js
new file mode 100644
index 00000000..5c75e586
--- /dev/null
+++ b/src/modules/overlay/plot/AttackArrow.js
@@ -0,0 +1,210 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-29
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import Parse from '../../parse/Parse'
+import State from '../../state/State'
+import { Transform } from '../../transform'
+import { Util, PlotUtil } from '../../utils'
+
+const HALF_PI = Math.PI / 2
+
+class AttackArrow extends Overlay {
+ constructor(positions) {
+ super()
+ this._positions = Parse.parsePositions(positions)
+ this._delegate = new Cesium.Entity({ polygon: {} })
+ this.headHeightFactor = 0.18
+ this.headWidthFactor = 0.3
+ this.neckHeightFactor = 0.85
+ this.neckWidthFactor = 0.15
+ this.headTailFactor = 0.8
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('attack_arrow')
+ }
+
+ set positions(positions) {
+ this._positions = Parse.parsePositions(positions)
+ this._delegate.polygon.hierarchy = this._getHierarchy()
+ }
+
+ get positions() {
+ return this._positions
+ }
+
+ _getArrowHeadPoints(points, tailLeft, tailRight) {
+ let len = PlotUtil.getBaseLength(points)
+ let headHeight = len * this.headHeightFactor
+ let headPnt = points[points.length - 1]
+ len = PlotUtil.distance(headPnt, points[points.length - 2])
+ let tailWidth = PlotUtil.distance(tailLeft, tailRight)
+ if (headHeight > tailWidth * this.headTailFactor) {
+ headHeight = tailWidth * this.headTailFactor
+ }
+ let headWidth = headHeight * this.headWidthFactor
+ let neckWidth = headHeight * this.neckWidthFactor
+ headHeight = headHeight > len ? len : headHeight
+ let neckHeight = headHeight * this.neckHeightFactor
+ let headEndPnt = PlotUtil.getThirdPoint(
+ points[points.length - 2],
+ headPnt,
+ 0,
+ headHeight,
+ true
+ )
+ let neckEndPnt = PlotUtil.getThirdPoint(
+ points[points.length - 2],
+ headPnt,
+ 0,
+ neckHeight,
+ true
+ )
+ let headLeft = PlotUtil.getThirdPoint(
+ headPnt,
+ headEndPnt,
+ HALF_PI,
+ headWidth,
+ false
+ )
+ let headRight = PlotUtil.getThirdPoint(
+ headPnt,
+ headEndPnt,
+ HALF_PI,
+ headWidth,
+ true
+ )
+ let neckLeft = PlotUtil.getThirdPoint(
+ headPnt,
+ neckEndPnt,
+ HALF_PI,
+ neckWidth,
+ false
+ )
+ let neckRight = PlotUtil.getThirdPoint(
+ headPnt,
+ neckEndPnt,
+ HALF_PI,
+ neckWidth,
+ true
+ )
+ return [neckLeft, headLeft, headPnt, headRight, neckRight]
+ }
+
+ _getArrowBodyPoints(points, neckLeft, neckRight, tailWidthFactor) {
+ let allLen = PlotUtil.wholeDistance(points)
+ let len = PlotUtil.getBaseLength(points)
+ let tailWidth = len * tailWidthFactor
+ let neckWidth = PlotUtil.distance(neckLeft, neckRight)
+ let widthDif = (tailWidth - neckWidth) / 2
+ let tempLen = 0
+ let leftBodyPnts = []
+ let rightBodyPnts = []
+ for (let i = 1; i < points.length - 1; i++) {
+ let angle =
+ PlotUtil.getAngleOfThreePoints(
+ points[i - 1],
+ points[i],
+ points[i + 1]
+ ) / 2
+ tempLen += PlotUtil.distance(points[i - 1], points[i])
+ let w = (tailWidth / 2 - (tempLen / allLen) * widthDif) / Math.sin(angle)
+ let left = PlotUtil.getThirdPoint(
+ points[i - 1],
+ points[i],
+ Math.PI - angle,
+ w,
+ true
+ )
+ let right = PlotUtil.getThirdPoint(
+ points[i - 1],
+ points[i],
+ angle,
+ w,
+ false
+ )
+ leftBodyPnts.push(left)
+ rightBodyPnts.push(right)
+ }
+ return leftBodyPnts.concat(rightBodyPnts)
+ }
+
+ _getHierarchy() {
+ let pnts = Parse.parsePolygonCoordToArray(this._positions)[0]
+ let tailLeft = pnts[0]
+ let tailRight = pnts[1]
+ if (PlotUtil.isClockWise(pnts[0], pnts[1], pnts[2])) {
+ tailLeft = pnts[1]
+ tailRight = pnts[0]
+ }
+ let midTail = PlotUtil.mid(tailLeft, tailRight)
+ let bonePnts = [midTail].concat(pnts.slice(2))
+ // 计算箭头
+ let headPnts = this._getArrowHeadPoints(bonePnts, tailLeft, tailRight)
+ let neckLeft = headPnts[0]
+ let neckRight = headPnts[4]
+ let tailWidthFactor =
+ PlotUtil.distance(tailLeft, tailRight) / PlotUtil.getBaseLength(bonePnts)
+ // 计算箭身
+ let bodyPnts = this._getArrowBodyPoints(
+ bonePnts,
+ neckLeft,
+ neckRight,
+ tailWidthFactor
+ )
+ // 整合
+ let count = bodyPnts.length
+ let leftPnts = [tailLeft].concat(bodyPnts.slice(0, count / 2))
+ leftPnts.push(neckLeft)
+ let rightPnts = [tailRight].concat(bodyPnts.slice(count / 2, count))
+ rightPnts.push(neckRight)
+ leftPnts = PlotUtil.getQBSplinePoints(leftPnts)
+ rightPnts = PlotUtil.getQBSplinePoints(rightPnts)
+ return new Cesium.PolygonHierarchy(
+ Transform.transformWGS84ArrayToCartesianArray(
+ Parse.parsePositions(leftPnts.concat(headPnts, rightPnts.reverse()))
+ )
+ )
+ }
+
+ _mountedHook() {
+ /**
+ * set the location
+ */
+ this.positions = this._positions
+ }
+
+ /**
+ *
+ * @param text
+ * @param textStyle
+ * @returns {AttackArrow}
+ */
+ setLabel(text, textStyle) {
+ return this
+ }
+
+ /**
+ * Sets Style
+ * @param style
+ * @returns {AttackArrow}
+ */
+ setStyle(style) {
+ if (Object.keys(style).length === 0) {
+ return this
+ }
+ delete style['positions']
+ Util.merge(this._style, style)
+ Util.merge(this._delegate.polygon, style)
+ return this
+ }
+}
+
+Overlay.registerType('attack_arrow')
+
+export default AttackArrow
diff --git a/src/modules/overlay/plot/DoubleArrow.js b/src/modules/overlay/plot/DoubleArrow.js
new file mode 100644
index 00000000..a80c9cdf
--- /dev/null
+++ b/src/modules/overlay/plot/DoubleArrow.js
@@ -0,0 +1,281 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-29 22:15:47
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import Parse from '../../parse/Parse'
+import State from '../../state/State'
+import { Transform } from '../../transform'
+import { Util, PlotUtil } from '../../utils'
+
+const HALF_PI = Math.PI / 2
+
+class DoubleArrow extends Overlay {
+ constructor(positions) {
+ super()
+ this._positions = Parse.parsePositions(positions)
+ this._delegate = new Cesium.Entity({ polygon: {} })
+ this.headHeightFactor = 0.25
+ this.headWidthFactor = 0.3
+ this.neckHeightFactor = 0.85
+ this.neckWidthFactor = 0.15
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('double_arrow')
+ }
+
+ set positions(positions) {
+ this._positions = Parse.parsePositions(positions)
+ this._delegate.polygon.hierarchy = this._getHierarchy()
+ }
+
+ get positions() {
+ return this._positions
+ }
+
+ _getArrowPoints(pnt1, pnt2, pnt3, clockWise) {
+ let midPnt = PlotUtil.mid(pnt1, pnt2)
+ let len = PlotUtil.distance(midPnt, pnt3)
+ let midPnt1 = PlotUtil.getThirdPoint(pnt3, midPnt, 0, len * 0.3, true)
+ let midPnt2 = PlotUtil.getThirdPoint(pnt3, midPnt, 0, len * 0.5, true)
+ midPnt1 = PlotUtil.getThirdPoint(
+ midPnt,
+ midPnt1,
+ HALF_PI,
+ len / 5,
+ clockWise
+ )
+ midPnt2 = PlotUtil.getThirdPoint(
+ midPnt,
+ midPnt2,
+ HALF_PI,
+ len / 4,
+ clockWise
+ )
+ let points = [midPnt, midPnt1, midPnt2, pnt3]
+ // 计算箭头部分
+ let arrowPnts = this._getArrowHeadPoints(points)
+ let neckLeftPoint = arrowPnts[0]
+ let neckRightPoint = arrowPnts[4]
+ // 计算箭身部分
+ let tailWidthFactor =
+ PlotUtil.distance(pnt1, pnt2) / PlotUtil.getBaseLength(points) / 2
+ let bodyPnts = this._getArrowBodyPoints(
+ points,
+ neckLeftPoint,
+ neckRightPoint,
+ tailWidthFactor
+ )
+ let n = bodyPnts.length
+ let lPoints = bodyPnts.slice(0, n / 2)
+ let rPoints = bodyPnts.slice(n / 2, n)
+ lPoints.push(neckLeftPoint)
+ rPoints.push(neckRightPoint)
+ lPoints = lPoints.reverse()
+ lPoints.push(pnt2)
+ rPoints = rPoints.reverse()
+ rPoints.push(pnt1)
+ return lPoints.reverse().concat(arrowPnts, rPoints)
+ }
+
+ _getArrowHeadPoints(points) {
+ let len = PlotUtil.getBaseLength(points)
+ let headHeight = len * this.headHeightFactor
+ let headPnt = points[points.length - 1]
+ let headWidth = headHeight * this.headWidthFactor
+ let neckWidth = headHeight * this.neckWidthFactor
+ let neckHeight = headHeight * this.neckHeightFactor
+ let headEndPnt = PlotUtil.getThirdPoint(
+ points[points.length - 2],
+ headPnt,
+ 0,
+ headHeight,
+ true
+ )
+ let neckEndPnt = PlotUtil.getThirdPoint(
+ points[points.length - 2],
+ headPnt,
+ 0,
+ neckHeight,
+ true
+ )
+ let headLeft = PlotUtil.getThirdPoint(
+ headPnt,
+ headEndPnt,
+ HALF_PI,
+ headWidth,
+ false
+ )
+ let headRight = PlotUtil.getThirdPoint(
+ headPnt,
+ headEndPnt,
+ HALF_PI,
+ headWidth,
+ true
+ )
+ let neckLeft = PlotUtil.getThirdPoint(
+ headPnt,
+ neckEndPnt,
+ HALF_PI,
+ neckWidth,
+ false
+ )
+ let neckRight = PlotUtil.getThirdPoint(
+ headPnt,
+ neckEndPnt,
+ HALF_PI,
+ neckWidth,
+ true
+ )
+ return [neckLeft, headLeft, headPnt, headRight, neckRight]
+ }
+
+ _getArrowBodyPoints(points, neckLeft, neckRight, tailWidthFactor) {
+ let allLen = PlotUtil.wholeDistance(points)
+ let len = PlotUtil.getBaseLength(points)
+ let tailWidth = len * tailWidthFactor
+ let neckWidth = PlotUtil.distance(neckLeft, neckRight)
+ let widthDif = (tailWidth - neckWidth) / 2
+ let tempLen = 0
+ let leftBodyPnts = []
+ let rightBodyPnts = []
+ for (let i = 1; i < points.length - 1; i++) {
+ let angle =
+ PlotUtil.getAngleOfThreePoints(
+ points[i - 1],
+ points[i],
+ points[i + 1]
+ ) / 2
+ tempLen += PlotUtil.distance(points[i - 1], points[i])
+ let w = (tailWidth / 2 - (tempLen / allLen) * widthDif) / Math.sin(angle)
+ let left = PlotUtil.getThirdPoint(
+ points[i - 1],
+ points[i],
+ Math.PI - angle,
+ w,
+ true
+ )
+ let right = PlotUtil.getThirdPoint(
+ points[i - 1],
+ points[i],
+ angle,
+ w,
+ false
+ )
+ leftBodyPnts.push(left)
+ rightBodyPnts.push(right)
+ }
+ return leftBodyPnts.concat(rightBodyPnts)
+ }
+
+ _getTempPoint4(linePnt1, linePnt2, point) {
+ let midPnt = PlotUtil.mid(linePnt1, linePnt2)
+ let len = PlotUtil.distance(midPnt, point)
+ let angle = PlotUtil.getAngleOfThreePoints(linePnt1, midPnt, point)
+ let symPnt, distance1, distance2, mid
+ if (angle < HALF_PI) {
+ distance1 = len * Math.sin(angle)
+ distance2 = len * Math.cos(angle)
+ mid = PlotUtil.getThirdPoint(linePnt1, midPnt, HALF_PI, distance1, false)
+ symPnt = PlotUtil.getThirdPoint(midPnt, mid, HALF_PI, distance2, true)
+ } else if (angle >= HALF_PI && angle < Math.PI) {
+ distance1 = len * Math.sin(Math.PI - angle)
+ distance2 = len * Math.cos(Math.PI - angle)
+ mid = PlotUtil.getThirdPoint(linePnt1, midPnt, HALF_PI, distance1, false)
+ symPnt = PlotUtil.getThirdPoint(midPnt, mid, HALF_PI, distance2, false)
+ } else if (angle >= Math.PI && angle < Math.PI * 1.5) {
+ distance1 = len * Math.sin(angle - Math.PI)
+ distance2 = len * Math.cos(angle - Math.PI)
+ mid = PlotUtil.getThirdPoint(linePnt1, midPnt, HALF_PI, distance1, true)
+ symPnt = PlotUtil.getThirdPoint(midPnt, mid, HALF_PI, distance2, true)
+ } else {
+ distance1 = len * Math.sin(Math.PI * 2 - angle)
+ distance2 = len * Math.cos(Math.PI * 2 - angle)
+ mid = PlotUtil.getThirdPoint(linePnt1, midPnt, HALF_PI, distance1, true)
+ symPnt = PlotUtil.getThirdPoint(midPnt, mid, HALF_PI, distance2, false)
+ }
+ return symPnt
+ }
+
+ _getHierarchy() {
+ let count = this._positions.length
+ let tempPoint4 = undefined
+ let connPoint = undefined
+ let pnts = Parse.parsePolygonCoordToArray(this._positions)[0]
+ let pnt1 = pnts[0]
+ let pnt2 = pnts[1]
+ let pnt3 = pnts[2]
+ if (count === 3) tempPoint4 = this._getTempPoint4(pnt1, pnt2, pnt3)
+ else tempPoint4 = pnts[3]
+ if (count === 3 || count === 4) connPoint = PlotUtil.mid(pnt1, pnt2)
+ else connPoint = pnts[4]
+ let leftArrowPnts, rightArrowPnts
+ if (PlotUtil.isClockWise(pnt1, pnt2, pnt3)) {
+ leftArrowPnts = this._getArrowPoints(pnt1, connPoint, tempPoint4, false)
+ rightArrowPnts = this._getArrowPoints(connPoint, pnt2, pnt3, true)
+ } else {
+ leftArrowPnts = this._getArrowPoints(pnt2, connPoint, pnt3, false)
+ rightArrowPnts = this._getArrowPoints(connPoint, pnt1, tempPoint4, true)
+ }
+ let m = leftArrowPnts.length
+ let t = (m - 5) / 2
+ let llBodyPnts = leftArrowPnts.slice(0, t)
+ let lArrowPnts = leftArrowPnts.slice(t, t + 5)
+ let lrBodyPnts = leftArrowPnts.slice(t + 5, m)
+ let rlBodyPnts = rightArrowPnts.slice(0, t)
+ let rArrowPnts = rightArrowPnts.slice(t, t + 5)
+ let rrBodyPnts = rightArrowPnts.slice(t + 5, m)
+ rlBodyPnts = PlotUtil.getBezierPoints(rlBodyPnts)
+ let bodyPnts = PlotUtil.getBezierPoints(
+ rrBodyPnts.concat(llBodyPnts.slice(1))
+ )
+ lrBodyPnts = PlotUtil.getBezierPoints(lrBodyPnts)
+ return new Cesium.PolygonHierarchy(
+ Transform.transformWGS84ArrayToCartesianArray(
+ Parse.parsePositions(
+ rlBodyPnts.concat(rArrowPnts, bodyPnts, lArrowPnts, lrBodyPnts)
+ )
+ )
+ )
+ }
+
+ _mountedHook() {
+ /**
+ * set the location
+ */
+ this.positions = this._positions
+ }
+
+ /**
+ *
+ * @param text
+ * @param textStyle
+ * @returns {DoubleArrow}
+ */
+ setLabel(text, textStyle) {
+ return this
+ }
+
+ /**
+ * Sets Style
+ * @param style
+ * @returns {DoubleArrow}
+ */
+ setStyle(style) {
+ if (Object.keys(style).length === 0) {
+ return this
+ }
+ delete style['positions']
+ Util.merge(this._style, style)
+ Util.merge(this._delegate.polygon, style)
+ return this
+ }
+}
+
+Overlay.registerType('double_arrow')
+
+export default DoubleArrow
diff --git a/src/modules/overlay/plot/FineArrow.js b/src/modules/overlay/plot/FineArrow.js
new file mode 100644
index 00000000..a69cf4de
--- /dev/null
+++ b/src/modules/overlay/plot/FineArrow.js
@@ -0,0 +1,135 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-29 22:38:10
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import Parse from '../../parse/Parse'
+import State from '../../state/State'
+import { Transform } from '../../transform'
+import { Util, PlotUtil } from '../../utils'
+
+const HALF_PI = Math.PI / 2
+
+class FineArrow extends Overlay {
+ constructor(positions) {
+ super()
+ this._positions = Parse.parsePositions(positions)
+ this._delegate = new Cesium.Entity({ polygon: {} })
+ this.tailWidthFactor = 0.15
+ this.neckWidthFactor = 0.2
+ this.headWidthFactor = 0.25
+ this.headAngle = Math.PI / 8.5
+ this.neckAngle = Math.PI / 13
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('fine_arrow')
+ }
+
+ set positions(positions) {
+ this._positions = Parse.parsePositions(positions)
+ this._delegate.polygon.hierarchy = this._getHierarchy()
+ }
+
+ get positions() {
+ return this._positions
+ }
+
+ _getHierarchy() {
+ let pnts = Parse.parsePolygonCoordToArray(this._positions)[0]
+ let pnt1 = pnts[0]
+ let pnt2 = pnts[1]
+ let len = PlotUtil.getBaseLength(pnts)
+ let tailWidth = len * this.tailWidthFactor
+ let neckWidth = len * this.neckWidthFactor
+ let headWidth = len * this.headWidthFactor
+ let tailLeft = PlotUtil.getThirdPoint(pnt2, pnt1, HALF_PI, tailWidth, true)
+ let tailRight = PlotUtil.getThirdPoint(
+ pnt2,
+ pnt1,
+ HALF_PI,
+ tailWidth,
+ false
+ )
+ let headLeft = PlotUtil.getThirdPoint(
+ pnt1,
+ pnt2,
+ this.headAngle,
+ headWidth,
+ false
+ )
+ let headRight = PlotUtil.getThirdPoint(
+ pnt1,
+ pnt2,
+ this.headAngle,
+ headWidth,
+ true
+ )
+ let neckLeft = PlotUtil.getThirdPoint(
+ pnt1,
+ pnt2,
+ this.neckAngle,
+ neckWidth,
+ false
+ )
+ let neckRight = PlotUtil.getThirdPoint(
+ pnt1,
+ pnt2,
+ this.neckAngle,
+ neckWidth,
+ true
+ )
+ return new Cesium.PolygonHierarchy(
+ Transform.transformWGS84ArrayToCartesianArray(
+ Parse.parsePositions([
+ tailLeft,
+ neckLeft,
+ headLeft,
+ pnt2,
+ headRight,
+ neckRight,
+ tailRight,
+ ])
+ )
+ )
+ }
+
+ _mountedHook() {
+ /**
+ * set the location
+ */
+ this.positions = this._positions
+ }
+
+ /**
+ *
+ * @param text
+ * @param textStyle
+ * @returns {FineArrow}
+ */
+ setLabel(text, textStyle) {
+ return this
+ }
+
+ /**
+ * Sets Style
+ * @param style
+ * @returns {FineArrow}
+ */
+ setStyle(style) {
+ if (Object.keys(style).length === 0) {
+ return this
+ }
+ delete style['positions']
+ Util.merge(this._style, style)
+ Util.merge(this._delegate.polygon, style)
+ return this
+ }
+}
+
+Overlay.registerType('fine_arrow')
+
+export default FineArrow
diff --git a/src/modules/overlay/plot/GatheringPlace.js b/src/modules/overlay/plot/GatheringPlace.js
new file mode 100644
index 00000000..47974ffa
--- /dev/null
+++ b/src/modules/overlay/plot/GatheringPlace.js
@@ -0,0 +1,116 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-29 23:00:27
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import Parse from '../../parse/Parse'
+import State from '../../state/State'
+import { Transform } from '../../transform'
+import { Util, PlotUtil } from '../../utils'
+
+const HALF_PI = Math.PI / 2
+
+const FITTING_COUNT = 100
+
+class GatheringPlace extends Overlay {
+ constructor(positions) {
+ super()
+ this._positions = Parse.parsePositions(positions)
+ this._delegate = new Cesium.Entity({ polygon: {} })
+ this.t = 0.4
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('gathering_place')
+ }
+
+ set positions(positions) {
+ this._positions = Parse.parsePositions(positions)
+ this._delegate.polygon.hierarchy = this._getHierarchy()
+ }
+
+ get positions() {
+ return this._positions
+ }
+
+ _getHierarchy() {
+ let pnts = Parse.parsePolygonCoordToArray(this._positions)[0]
+ if (this._positions.length === 2) {
+ let mid = PlotUtil.mid(pnts[0], pnts[1])
+ let d = PlotUtil.distance(pnts[0], mid) / 0.9
+ let pnt = PlotUtil.getThirdPoint(pnts[0], mid, HALF_PI, d, true)
+ pnts = [pnts[0], pnt, pnts[1]]
+ }
+ let mid = PlotUtil.mid(pnts[0], pnts[2])
+ pnts.push(mid, pnts[0], pnts[1])
+ let normals = []
+ for (let i = 0; i < pnts.length - 2; i++) {
+ let pnt1 = pnts[i]
+ let pnt2 = pnts[i + 1]
+ let pnt3 = pnts[i + 2]
+ let normalPoints = PlotUtil.getBisectorNormals(this.t, pnt1, pnt2, pnt3)
+ normals = normals.concat(normalPoints)
+ }
+ let count = normals.length
+ normals = [normals[count - 1]].concat(normals.slice(0, count - 1))
+ let pList = []
+ for (let i = 0; i < pnts.length - 2; i++) {
+ let pnt1 = pnts[i]
+ let pnt2 = pnts[i + 1]
+ pList.push(pnt1)
+ for (let t = 0; t <= FITTING_COUNT; t++) {
+ let pnt = PlotUtil.getCubicValue(
+ t / FITTING_COUNT,
+ pnt1,
+ normals[i * 2],
+ normals[i * 2 + 1],
+ pnt2
+ )
+ pList.push(pnt)
+ }
+ pList.push(pnt2)
+ }
+ return new Cesium.PolygonHierarchy(
+ Transform.transformWGS84ArrayToCartesianArray(Parse.parsePositions(pList))
+ )
+ }
+
+ _mountedHook() {
+ /**
+ * set the location
+ */
+ this.positions = this._positions
+ }
+
+ /**
+ *
+ * @param text
+ * @param textStyle
+ * @returns {GatheringPlace}
+ */
+ setLabel(text, textStyle) {
+ return this
+ }
+
+ /**
+ * Sets Style
+ * @param style
+ * @returns {GatheringPlace}
+ */
+ setStyle(style) {
+ if (Object.keys(style).length === 0) {
+ return this
+ }
+ delete style['positions']
+ Util.merge(this._style, style)
+ Util.merge(this._delegate.polygon, style)
+ return this
+ }
+}
+
+Overlay.registerType('gathering_place')
+
+export default GatheringPlace
diff --git a/src/modules/overlay/plot/TailedAttackArrow.js b/src/modules/overlay/plot/TailedAttackArrow.js
new file mode 100644
index 00000000..84b613a7
--- /dev/null
+++ b/src/modules/overlay/plot/TailedAttackArrow.js
@@ -0,0 +1,93 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-29 22:51:36
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import Parse from '../../parse/Parse'
+import State from '../../state/State'
+import { Transform } from '../../transform'
+import { PlotUtil } from '../../utils'
+import AttackArrow from './AttackArrow'
+
+class TailedAttackArrow extends AttackArrow {
+ constructor(positions) {
+ super(positions)
+ this._delegate = new Cesium.Entity({ polygon: {} })
+ this.headHeightFactor = 0.18
+ this.headWidthFactor = 0.3
+ this.neckHeightFactor = 0.85
+ this.neckWidthFactor = 0.15
+ this.tailWidthFactor = 0.1
+ this.headTailFactor = 0.8
+ this.swallowTailFactor = 1
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('tailed_attack_arrow')
+ }
+
+ set positions(positions) {
+ this._positions = Parse.parsePositions(positions)
+ this._delegate.polygon.hierarchy = this._getHierarchy()
+ }
+
+ get positions() {
+ return this._positions
+ }
+
+ _getHierarchy() {
+ let pnts = Parse.parsePolygonCoordToArray(this._positions)[0]
+ let tailLeft = pnts[0]
+ let tailRight = pnts[1]
+ if (PlotUtil.isClockWise(pnts[0], pnts[1], pnts[2])) {
+ tailLeft = pnts[1]
+ tailRight = pnts[0]
+ }
+ let midTail = PlotUtil.mid(tailLeft, tailRight)
+ let bonePnts = [midTail].concat(pnts.slice(2))
+ let headPnts = this._getArrowHeadPoints(bonePnts, tailLeft, tailRight)
+ let neckLeft = headPnts[0]
+ let neckRight = headPnts[4]
+ let tailWidth = PlotUtil.distance(tailLeft, tailRight)
+ let allLen = PlotUtil.getBaseLength(bonePnts)
+ let len = allLen * this.tailWidthFactor * this.swallowTailFactor
+ let swallowTailPnt = PlotUtil.getThirdPoint(
+ bonePnts[1],
+ bonePnts[0],
+ 0,
+ len,
+ true
+ )
+ let factor = tailWidth / allLen
+ let bodyPnts = this._getArrowBodyPoints(
+ bonePnts,
+ neckLeft,
+ neckRight,
+ factor
+ )
+ let count = bodyPnts.length
+ let leftPnts = [tailLeft].concat(bodyPnts.slice(0, count / 2))
+ leftPnts.push(neckLeft)
+ let rightPnts = [tailRight].concat(bodyPnts.slice(count / 2, count))
+ rightPnts.push(neckRight)
+ leftPnts = PlotUtil.getQBSplinePoints(leftPnts)
+ rightPnts = PlotUtil.getQBSplinePoints(rightPnts)
+ return new Cesium.PolygonHierarchy(
+ Transform.transformWGS84ArrayToCartesianArray(
+ Parse.parsePositions(
+ leftPnts.concat(headPnts, rightPnts.reverse(), [
+ swallowTailPnt,
+ leftPnts[0],
+ ])
+ )
+ )
+ )
+ }
+}
+
+Overlay.registerType('tailed_attack_arrow')
+
+export default TailedAttackArrow
diff --git a/src/modules/overlay/primitive/BillboardPrimitive.js b/src/modules/overlay/primitive/BillboardPrimitive.js
new file mode 100644
index 00000000..b24bd66f
--- /dev/null
+++ b/src/modules/overlay/primitive/BillboardPrimitive.js
@@ -0,0 +1,97 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-06-03 21:06:17
+ */
+
+import Overlay from '../Overlay'
+import Parse from '../../parse/Parse'
+import State from '../../state/State'
+import { Transform } from '../../transform'
+import { Util } from '../../utils'
+
+class BillboardPrimitive extends Overlay {
+ constructor(position, icon) {
+ super()
+ this._position = Parse.parsePosition(position)
+ this._icon = icon
+ this._size = [32, 32]
+ this._delegate = {
+ position: undefined,
+ image: undefined,
+ width: 0,
+ height: 0,
+ }
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('billboard_primitive')
+ }
+
+ set position(position) {
+ this._position = Parse.parsePosition(position)
+ this._delegate.position = Transform.transformWGS84ToCartesian(
+ this._position
+ )
+ }
+
+ get position() {
+ return this._position
+ }
+
+ set icon(icon) {
+ this._icon = icon
+ this._delegate.image = this._icon
+ }
+
+ get icon() {
+ return this._icon
+ }
+
+ set size(size) {
+ if (!Array.isArray(size)) {
+ throw new Error('Billboard Primitive: the size invalid')
+ }
+ this._size = size
+ this._delegate.width = this._size[0] || 32
+ this._delegate.height = this._size[1] || 32
+ }
+
+ get size() {
+ return this._size
+ }
+
+ _mountedHook() {
+ /**
+ * set the location
+ */
+ this.position = this._position
+ /**
+ * initialize the Overlay parameter
+ */
+ this.icon = this._icon
+ this.size = this._size
+ }
+
+ /**
+ *
+ * @param style
+ * @returns {BillboardPrimitive}
+ */
+ setStyle(style) {
+ if (!style || Object.keys(style).length === 0) {
+ return this
+ }
+ delete style['position'] &&
+ delete style['image'] &&
+ delete style['width'] &&
+ delete style['height']
+ Util.merge(this._style, style)
+ Util.merge(this._delegate, style)
+ return this
+ }
+}
+
+Overlay.registerType('billboard_primitive')
+
+export default BillboardPrimitive
diff --git a/src/modules/overlay/primitive/BounceBillboardPrimitive.js b/src/modules/overlay/primitive/BounceBillboardPrimitive.js
new file mode 100644
index 00000000..870fa2b3
--- /dev/null
+++ b/src/modules/overlay/primitive/BounceBillboardPrimitive.js
@@ -0,0 +1,57 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-06-03 21:06:17
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import State from '../../state/State'
+import BillboardPrimitive from './BillboardPrimitive'
+
+const DEF_STYLE = {
+ maxOffsetY: 10,
+ offsetAmount: 0.1,
+}
+
+class BounceBillboardPrimitive extends BillboardPrimitive {
+ constructor(position, icon) {
+ super(position, icon)
+ this._currentOffset = new Cesium.Cartesian2(0, 0)
+ this._isUp = true
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('bounce_billboard_primitive')
+ }
+
+ /**
+ *
+ * @param frameState
+ */
+ update(frameState) {
+ if (!this._show) {
+ return
+ }
+ let maxOffsetY = this._style?.maxOffsetY || DEF_STYLE.maxOffsetY
+ let offsetAmount = this._style?.offsetAmount || DEF_STYLE.offsetAmount
+ if (this._currentOffset.y >= 0) {
+ this._isUp = true
+ } else if (this._currentOffset.y <= -maxOffsetY) {
+ this._isUp = false
+ }
+ this._currentOffset.y += offsetAmount * (this._isUp ? -1 : 1)
+ this._delegate.pixelOffset = this._currentOffset
+ }
+
+ /**
+ * @return {*}
+ */
+ destroy() {
+ return Cesium.destroyObject(this)
+ }
+}
+
+Overlay.registerType('bounce_billboard_primitive')
+
+export default BounceBillboardPrimitive
diff --git a/src/modules/overlay/primitive/BounceLabelPrimitive.js b/src/modules/overlay/primitive/BounceLabelPrimitive.js
new file mode 100644
index 00000000..cf69d0f4
--- /dev/null
+++ b/src/modules/overlay/primitive/BounceLabelPrimitive.js
@@ -0,0 +1,57 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-06-03 21:06:17
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import State from '../../state/State'
+import LabelPrimitive from './LabelPrimitive'
+
+const DEF_STYLE = {
+ maxOffsetY: 10,
+ offsetAmount: 0.1,
+}
+
+class BounceLabelPrimitive extends LabelPrimitive {
+ constructor(position, text) {
+ super(position, text)
+ this._currentOffset = new Cesium.Cartesian2(0, 0)
+ this._isUp = true
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('bounce_label_primitive')
+ }
+
+ /**
+ *
+ * @param frameState
+ */
+ update(frameState) {
+ if (!this._show) {
+ return
+ }
+ let maxOffsetY = this._style?.maxOffsetY || DEF_STYLE.maxOffsetY
+ let offsetAmount = this._style?.offsetAmount || DEF_STYLE.offsetAmount
+ if (this._currentOffset.y >= 0) {
+ this._isUp = true
+ } else if (this._currentOffset.y <= -maxOffsetY) {
+ this._isUp = false
+ }
+ this._currentOffset.y += offsetAmount * (this._isUp ? -1 : 1)
+ this._delegate.pixelOffset = this._currentOffset
+ }
+
+ /**
+ * @return {*}
+ */
+ destroy() {
+ return Cesium.destroyObject(this)
+ }
+}
+
+Overlay.registerType('bounce_label_primitive')
+
+export default BounceLabelPrimitive
diff --git a/src/modules/overlay/primitive/CloudPrimitive.js b/src/modules/overlay/primitive/CloudPrimitive.js
new file mode 100644
index 00000000..b0fb8835
--- /dev/null
+++ b/src/modules/overlay/primitive/CloudPrimitive.js
@@ -0,0 +1,63 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-11-08 20:35:42
+ */
+
+import Overlay from '../Overlay'
+import State from '../../state/State'
+import Parse from '../../parse/Parse'
+import { Transform } from '../../transform'
+import { Util } from '../../utils'
+
+class CloudPrimitive extends Overlay {
+ constructor(position) {
+ super()
+ this._position = Parse.parsePosition(position)
+ this._delegate = {
+ position: undefined,
+ scale: { x: 12, y: 8 },
+ }
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('cloud_primitive')
+ }
+
+ set position(position) {
+ this._position = Parse.parsePosition(position)
+ this._delegate.position = Transform.transformWGS84ToCartesian(
+ this._position
+ )
+ }
+
+ get position() {
+ return this._position
+ }
+
+ _mountedHook() {
+ /**
+ * set the location
+ */
+ this.position = this._position
+ }
+
+ /**
+ *
+ * @param style
+ * @returns {CloudPrimitive}
+ */
+ setStyle(style) {
+ if (!style || Object.keys(style).length === 0) {
+ return this
+ }
+ delete style['position']
+ Util.merge(this._style, style)
+ Util.merge(this._delegate, style)
+ return this
+ }
+}
+
+Overlay.registerType('cloud_primitive')
+
+export default CloudPrimitive
diff --git a/src/modules/overlay/primitive/DiffuseWallPrimitive.js b/src/modules/overlay/primitive/DiffuseWallPrimitive.js
new file mode 100644
index 00000000..e90bc5b6
--- /dev/null
+++ b/src/modules/overlay/primitive/DiffuseWallPrimitive.js
@@ -0,0 +1,178 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-06-04 20:38:39
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import State from '../../state/State'
+import Parse from '../../parse/Parse'
+import { Transform } from '../../transform'
+import { Util } from '../../utils'
+
+const DEF_STYLE = {
+ minRadius: 10,
+ minHeight: 30,
+ color: Cesium.Color.RED,
+ slices: 128,
+ speed: 10,
+}
+
+class DiffuseWallPrimitive extends Overlay {
+ constructor(center, radius, height) {
+ super()
+ this._center = Parse.parsePosition(center)
+ this._delegate = undefined
+ this._height = height
+ this._radius = radius
+ this._currentHeight = height || 0
+ this._currentRadius = 10
+ this._style = { ...DEF_STYLE }
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('diffuse_wall_primitive')
+ }
+
+ set center(position) {
+ this._center = Parse.parsePosition(position)
+ return this
+ }
+
+ get center() {
+ return this._center
+ }
+
+ set radius(radius) {
+ this._radius = radius
+ return this
+ }
+
+ get radius() {
+ return this._radius
+ }
+
+ set height(height) {
+ this._height = height
+ return this
+ }
+
+ get height() {
+ return this._height
+ }
+
+ /**
+ *
+ * @returns {*}
+ * @private
+ */
+ _getPositions() {
+ let pnts = []
+ let modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(
+ Transform.transformWGS84ToCartesian(this._center)
+ )
+ for (let i = 0; i < this._style.slices; i++) {
+ let angle = (i / this._style.slices) * Cesium.Math.TWO_PI
+ let x = Math.cos(angle)
+ let y = Math.sin(angle)
+ let point = new Cesium.Cartesian3(
+ x * this._currentRadius,
+ y * this._currentRadius,
+ 0.0
+ )
+ pnts.push(
+ Cesium.Matrix4.multiplyByPoint(
+ modelMatrix,
+ point,
+ new Cesium.Cartesian3()
+ )
+ )
+ }
+ pnts.push(pnts[0])
+ return pnts
+ }
+
+ /**
+ *
+ * @param length
+ * @param hegiht
+ * @returns {*[]}
+ * @private
+ */
+ _getHeights(length, hegiht) {
+ let heights = []
+ for (let i = 0; i < length; i++) {
+ heights.push(hegiht)
+ }
+ return heights
+ }
+
+ /**
+ *
+ * @param frameState
+ * @returns {boolean}
+ */
+ update(frameState) {
+ this._delegate = this._delegate && this._delegate.destroy()
+ this._currentRadius += this._radius / this._style.speed / 20
+ this._currentHeight -= this._height / this._style.speed / 20
+ if (
+ this._currentRadius > this._radius ||
+ this._currentHeight < this._style.minHeight
+ ) {
+ this._currentRadius = this._style.minRadius
+ this._currentHeight = this._height
+ }
+ if (!this._style.slices || this._style.slices < 3) {
+ return false
+ }
+ let positions = this._getPositions()
+ if (!positions || !positions.length) {
+ return false
+ }
+ let geometry = new Cesium.WallGeometry({
+ positions: positions,
+ minimumHeights: this._getHeights(positions.length, 0),
+ maximumHeights: this._getHeights(positions.length, this._currentHeight),
+ })
+
+ this._delegate = new Cesium.Primitive({
+ geometryInstances: new Cesium.GeometryInstance({
+ geometry,
+ }),
+ appearance: new Cesium.MaterialAppearance({
+ material: Cesium.Material.fromType('WallDiffuse', {
+ color: this._style?.color,
+ }),
+ flat: true,
+ }),
+ asynchronous: false,
+ })
+ this._delegate.update(frameState)
+ }
+
+ /**
+ *
+ * @param style
+ * @returns {DiffuseWallPrimitive}
+ */
+ setStyle(style) {
+ if (!style || Object.keys(style).length === 0) {
+ return this
+ }
+ Util.merge(this._style, style)
+ return this
+ }
+
+ /**
+ * @return {*}
+ */
+ destroy() {
+ return Cesium.destroyObject(this)
+ }
+}
+
+Overlay.registerType('diffuse_wall_primitive')
+
+export default DiffuseWallPrimitive
diff --git a/src/modules/overlay/primitive/ElecEllipsoidPrimitive.js b/src/modules/overlay/primitive/ElecEllipsoidPrimitive.js
new file mode 100644
index 00000000..5e878645
--- /dev/null
+++ b/src/modules/overlay/primitive/ElecEllipsoidPrimitive.js
@@ -0,0 +1,114 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-01-09 21:40:36
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import State from '../../state/State'
+import Parse from '../../parse/Parse'
+import { Transform } from '../../transform'
+import { Util } from '../../utils'
+
+class ElecEllipsoidPrimitive extends Overlay {
+ constructor(position, radius) {
+ super()
+ this._position = Parse.parsePosition(position)
+ this._radius = radius || { x: 10, y: 10, z: 10 }
+ this._delegate = new Cesium.Primitive({
+ geometryInstances: new Cesium.GeometryInstance({
+ geometry: {},
+ }),
+ })
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('elec_ellipsoid_primitive')
+ }
+
+ set position(position) {
+ this._position = Parse.parsePosition(position)
+ this._delegate.geometryInstances.modelMatrix =
+ Cesium.Transforms.eastNorthUpToFixedFrame(
+ Transform.transformWGS84ToCartesian(this._position)
+ )
+ }
+
+ get position() {
+ return this._position
+ }
+
+ set radius(radius) {
+ this._radius = radius
+ this._delegate.geometryInstances.geometry = new Cesium.EllipsoidGeometry({
+ radii: this._radius,
+ maximumCone: Cesium.Math.PI_OVER_TWO,
+ })
+ }
+
+ get radius() {
+ return this._radius
+ }
+
+ /**
+ *
+ * @private
+ */
+ _setAppearance() {
+ if (!this._style) {
+ return
+ }
+ this._delegate.appearance = new Cesium.MaterialAppearance({
+ material: Cesium.Material.fromType('EllipsoidElectric', {
+ color: this._style?.color || Cesium.Color.GREEN,
+ speed: this._style?.speed || 5,
+ }),
+ })
+ }
+
+ _mountedHook() {
+ /**
+ * set the radius
+ */
+ this.radius = this._radius
+
+ /**
+ * set the position
+ */
+ this.position = this._position
+
+ /**
+ * set the appearance
+ */
+ !this._delegate.appearance && this._setAppearance()
+ }
+
+ /**
+ *
+ * @param text
+ * @param textStyle
+ * @returns {ElecEllipsoidPrimitive}
+ */
+ setLabel(text, textStyle) {
+ return this
+ }
+
+ /**
+ * Sets Style
+ * @param style
+ * @returns {ElecEllipsoidPrimitive}
+ */
+ setStyle(style = {}) {
+ if (Object.keys(style).length === 0) {
+ return this
+ }
+ Util.merge(this._style, style)
+ this._setAppearance()
+ return this
+ }
+}
+
+Overlay.registerType('elec_ellipsoid_primitive')
+
+export default ElecEllipsoidPrimitive
diff --git a/src/modules/overlay/primitive/FlowLinePrimitive.js b/src/modules/overlay/primitive/FlowLinePrimitive.js
new file mode 100644
index 00000000..e2cf9399
--- /dev/null
+++ b/src/modules/overlay/primitive/FlowLinePrimitive.js
@@ -0,0 +1,97 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-01-05 20:18:34
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import State from '../../state/State'
+import Parse from '../../parse/Parse'
+import { Transform } from '../../transform'
+import { Util } from '../../utils'
+
+class FlowLinePrimitive extends Overlay {
+ constructor(positions, width = 1) {
+ super()
+ this._positions = Parse.parsePositions(positions)
+ this._width = width
+ this._delegate = new Cesium.Primitive({
+ geometryInstances: new Cesium.GeometryInstance({
+ geometry: {},
+ }),
+ })
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('flow_line_primitive')
+ }
+
+ set positions(positions) {
+ this._positions = Parse.parsePositions(positions)
+ this._delegate.geometryInstances.geometry = new Cesium.PolylineGeometry({
+ positions: Transform.transformWGS84ArrayToCartesianArray(this._positions),
+ width: this._width,
+ })
+ }
+
+ get positions() {
+ return this._positions
+ }
+
+ /**
+ *
+ * @private
+ */
+ _setAppearance() {
+ this._delegate.appearance = new Cesium.PolylineMaterialAppearance({
+ material: Cesium.Material.fromType('PolylineFlow', {
+ color: this._style?.color || new Cesium.Color(1.0, 0.0, 0.0, 0.7),
+ speed: this._style?.speed || 1,
+ percent: this._style?.percent || 0.03,
+ gradient: this._style?.gradient || 0.1,
+ }),
+ })
+ }
+
+ _mountedHook() {
+ /**
+ * set the positions
+ */
+ this.positions = this._positions
+ /**
+ * set the appearance
+ */
+ !this._delegate.appearance && this._setAppearance()
+ }
+
+ /**
+ *
+ * @param text
+ * @param textStyle
+ * @returns {FlowLinePrimitive}
+ */
+ setLabel(text, textStyle) {
+ return this
+ }
+
+ /**
+ * Sets Style
+ * @param style
+ * @returns {FlowLinePrimitive}
+ */
+ setStyle(style = {}) {
+ if (Object.keys(style).length === 0) {
+ return this
+ }
+ Util.merge(this._style, style)
+ style.classificationType &&
+ (this._delegate.classificationType = this._style.classificationType)
+ this._setAppearance()
+ return this
+ }
+}
+
+Overlay.registerType('flow_line_primitive')
+
+export default FlowLinePrimitive
diff --git a/src/modules/overlay/primitive/GroundPolygonPrimitive.js b/src/modules/overlay/primitive/GroundPolygonPrimitive.js
new file mode 100644
index 00000000..4b24b67b
--- /dev/null
+++ b/src/modules/overlay/primitive/GroundPolygonPrimitive.js
@@ -0,0 +1,8 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-12-01 20:31:02
+ */
+
+class GroundPolygonPrimitive {}
+
+export default GroundPolygonPrimitive
diff --git a/src/modules/overlay/primitive/GroundPolylinePrimitive.js b/src/modules/overlay/primitive/GroundPolylinePrimitive.js
new file mode 100644
index 00000000..ef824783
--- /dev/null
+++ b/src/modules/overlay/primitive/GroundPolylinePrimitive.js
@@ -0,0 +1,10 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-12-01 20:31:02
+ */
+
+class GroundPolylinePrimitive {
+ constructor(postions) {}
+}
+
+export default GroundPolylinePrimitive
diff --git a/src/modules/overlay/primitive/LabelPrimitive.js b/src/modules/overlay/primitive/LabelPrimitive.js
new file mode 100644
index 00000000..1abb38ad
--- /dev/null
+++ b/src/modules/overlay/primitive/LabelPrimitive.js
@@ -0,0 +1,87 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-06-03 22:28:49
+ */
+
+import Overlay from '../Overlay'
+import State from '../../state/State'
+import Parse from '../../parse/Parse'
+import { Transform } from '../../transform'
+import { Util } from '../../utils'
+
+class LabelPrimitive extends Overlay {
+ constructor(position, text) {
+ super()
+ this._position = Parse.parsePosition(position)
+ this._text = text
+ this._delegate = {
+ position: undefined,
+ text: undefined,
+ }
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('label_primitive')
+ }
+
+ set position(position) {
+ this._position = Parse.parsePosition(position)
+ this._delegate.position = Transform.transformWGS84ToCartesian(
+ this._position
+ )
+ }
+
+ get position() {
+ return this._position
+ }
+
+ set text(text) {
+ this._text = text
+ this._delegate.text = this._text
+ }
+
+ get text() {
+ return this._text
+ }
+
+ _mountedHook() {
+ /**
+ * set the location
+ */
+ this.position = this._position
+
+ /**
+ * initialize the Overlay parameter
+ */
+ this.text = this._text
+ }
+
+ /**
+ *
+ * @param {*} text
+ * @param {*} textStyle
+ */
+ setLabel(text, textStyle) {
+ return this
+ }
+
+ /**
+ * Sets Style
+ * @param style
+ * @returns {LabelPrimitive}
+ */
+ setStyle(style) {
+ if (!style || Object.keys(style).length === 0) {
+ return this
+ }
+ delete style['position'] && delete style['text']
+ Util.merge(this._style, style)
+ Util.merge(this._delegate, style)
+ return this
+ }
+}
+
+Overlay.registerType('label_primitive')
+
+export default LabelPrimitive
diff --git a/src/modules/overlay/primitive/LightCylinderPrimitive.js b/src/modules/overlay/primitive/LightCylinderPrimitive.js
new file mode 100644
index 00000000..b0013188
--- /dev/null
+++ b/src/modules/overlay/primitive/LightCylinderPrimitive.js
@@ -0,0 +1,327 @@
+/**
+ * @Author: Caven
+ * @Date: 2022-05-28 10:25:24
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import State from '../../state/State'
+import Parse from '../../parse/Parse'
+import { Transform } from '../../transform'
+import { Util } from '../../utils'
+import { IMG_PARTICLES } from '../../images/base64'
+
+const DEF_STYLE = {
+ color: Cesium.Color.ORANGE,
+}
+
+class LightCylinderPrimitive extends Overlay {
+ constructor(center, length, topRadius, bottomRadius) {
+ super()
+ this._center = Parse.parsePosition(center)
+ this._length = length || 100
+ this._topRadius = topRadius || 1
+ this._bottomRadius = bottomRadius || 50
+ this._delegate = new Cesium.PrimitiveCollection()
+ this._style = { ...DEF_STYLE }
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('light-cylinder-primitive')
+ }
+
+ set center(position) {
+ this._center = Parse.parsePosition(position)
+ this._updatePrimitives()
+ }
+
+ get center() {
+ return this._center
+ }
+
+ set length(length) {
+ this._length = length
+ this._updatePrimitives()
+ }
+
+ get length() {
+ return this._length
+ }
+
+ set topRadius(topRadius) {
+ this._topRadius = topRadius
+ this._updatePrimitives()
+ }
+
+ get topRadius() {
+ return this._topRadius
+ }
+
+ set bottomRadius(bottomRadius) {
+ this._bottomRadius = bottomRadius
+ this._updatePrimitives()
+ }
+
+ get bottomRadius() {
+ return this._bottomRadius
+ }
+
+ /**
+ *
+ * @param center
+ * @param radius
+ * @return {[]}
+ * @private
+ */
+ _computeEllipsePositions(center, radius) {
+ let cep = Cesium.EllipseGeometryLibrary.computeEllipsePositions(
+ {
+ center: Transform.transformWGS84ToCartesian(center),
+ semiMajorAxis: radius,
+ semiMinorAxis: radius,
+ rotation: 0,
+ granularity: 0.005,
+ },
+ false,
+ true
+ )
+ let pnts = Cesium.Cartesian3.unpackArray(cep.outerPositions)
+ pnts.push(pnts[0])
+ return pnts
+ }
+
+ /**
+ *
+ * @param topPts
+ * @param bottomPts
+ * @param height
+ * @return {Cesium.GeometryInstance}
+ */
+ _createCylinderInstance(topPts, bottomPts, height) {
+ let newpts = bottomPts.slice()
+ let length = bottomPts.length
+ let len_2 = 2 * length
+ let sts = []
+ let st_interval = 1.0 / (length - 1)
+ let define_indices = []
+ let ep = []
+ const addHeight = (p, alt = 0) => {
+ let c = Cesium.Cartographic.fromCartesian(p)
+ c.height += alt
+ return Cesium.Cartographic.toCartesian(c)
+ }
+ for (let i = 0; i < length; i++) {
+ ep.push(addHeight(topPts[i], height))
+ sts.push(i * st_interval, 0)
+ let i_1 = i + 1
+ let i_11 = (i + 1) % length
+ let len_2_i_1 = len_2 - i_1
+ define_indices.push(...[len_2_i_1 - 1, len_2_i_1, i])
+ define_indices.push(...[i, i_11, len_2_i_1 - 1])
+ }
+
+ for (let i in ep) {
+ newpts.push(ep[length - i - 1])
+ sts.push(1 - i * st_interval, 1)
+ }
+
+ let polygon = Cesium.PolygonGeometry.createGeometry(
+ new Cesium.PolygonGeometry({
+ polygonHierarchy: new Cesium.PolygonHierarchy(newpts),
+ perPositionHeight: true,
+ })
+ )
+ polygon.indices = define_indices
+ polygon.attributes.st.values = sts
+ return new Cesium.GeometryInstance({
+ geometry: polygon,
+ })
+ }
+
+ /**
+ *
+ * @return {HTMLCanvasElement}
+ * @private
+ */
+ _getCircleImage() {
+ let canvas = document.createElement('canvas')
+ canvas.width = 512
+ canvas.height = 512
+ let ctx = canvas.getContext('2d')
+ ctx.fillStyle = 'rgba(255,255,255,0)'
+ ctx.strokeStyle = 'rgba(255, 255, 255,255)'
+ ctx.setLineDash([50, 50])
+ ctx.lineWidth = 30
+ ctx.beginPath()
+ ctx.arc(256, 256, 150, 0, Math.PI * 2, true)
+ ctx.stroke()
+ ctx.restore()
+ return canvas
+ }
+
+ /**
+ *
+ * @param image
+ * @return {HTMLCanvasElement}
+ * @private
+ */
+ _getParticlesImage(image) {
+ let canvas = document.createElement('canvas')
+ canvas.width = 64
+ canvas.height = 256
+ let ctx = canvas.getContext('2d')
+ ctx.clearRect(0, 0, 64, 256)
+ ctx.drawImage(image, 0, 0)
+ ctx.drawImage(image, 33, 0)
+ return canvas
+ }
+
+ /**
+ *
+ * @private
+ */
+ _updatePrimitives() {
+ this._delegate.removeAll()
+
+ const isGroud = this._center.alt === 0
+
+ let topPositions = this._computeEllipsePositions(
+ this._center,
+ this._topRadius
+ )
+ let innerBottomPostions = this._computeEllipsePositions(
+ this._center,
+ this._bottomRadius * 0.7
+ )
+ let bottomPositions = this._computeEllipsePositions(
+ this._center,
+ this._bottomRadius
+ )
+
+ // update buttom circle
+ const circleOpt = {
+ geometryInstances: new Cesium.GeometryInstance({
+ geometry: new Cesium.PolygonGeometry({
+ polygonHierarchy: new Cesium.PolygonHierarchy(
+ this._computeEllipsePositions(this._center, this._bottomRadius * 2)
+ ),
+ perPositionHeight: !isGroud,
+ }),
+ asynchronous: false,
+ }),
+ }
+
+ // ring
+ let ring = isGroud
+ ? new Cesium.GroundPrimitive(circleOpt)
+ : new Cesium.Primitive(circleOpt)
+
+ ring.appearance = new Cesium.EllipsoidSurfaceAppearance({
+ material: Cesium.Material.fromType(Cesium.Material.CircleRingType, {
+ color: this._style.color,
+ }),
+ })
+
+ // circle
+ let circle = isGroud
+ ? new Cesium.GroundPrimitive(circleOpt)
+ : new Cesium.Primitive(circleOpt)
+
+ circle.appearance = new Cesium.EllipsoidSurfaceAppearance({
+ material: Cesium.Material.fromType(Cesium.Material.CircleRotateType, {
+ color: this._style.color,
+ image: this._getCircleImage(),
+ }),
+ })
+
+ // cylinder
+ let cylinder = new Cesium.Primitive({
+ geometryInstances: this._createCylinderInstance(
+ topPositions,
+ innerBottomPostions,
+ this._length
+ ),
+ appearance: new Cesium.EllipsoidSurfaceAppearance({
+ material: Cesium.Material.fromType(Cesium.Material.CylinderFadeType, {
+ color: this._style.color,
+ }),
+ }),
+ asynchronous: false,
+ })
+
+ if (isGroud) {
+ Cesium.GroundPrimitive.initializeTerrainHeights().then(() => {
+ this._delegate.add(ring)
+ this._delegate.add(circle)
+ this._delegate.add(cylinder)
+ })
+ } else {
+ this._delegate.add(ring)
+ this._delegate.add(circle)
+ this._delegate.add(cylinder)
+ }
+
+ // particles
+ Cesium.Resource.fetchImage({ url: IMG_PARTICLES }).then((image) => {
+ let particles = new Cesium.Primitive({
+ geometryInstances: this._createCylinderInstance(
+ topPositions,
+ bottomPositions,
+ this._length
+ ),
+ appearance: new Cesium.EllipsoidSurfaceAppearance({
+ material: Cesium.Material.fromType(
+ Cesium.Material.CylinderParticlesType,
+ {
+ color: this._style.color,
+ image: this._getParticlesImage(image),
+ }
+ ),
+ }),
+ asynchronous: false,
+ })
+ this._delegate.add(particles)
+ })
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountedHook() {
+ /**
+ * set the positions
+ */
+ this.center = this._center
+ }
+
+ /**
+ *
+ * @param frameState
+ */
+ update(frameState) {
+ this._delegate.update(frameState)
+ }
+
+ /**
+ *
+ * @param style
+ * @returns {LightCylinderPrimitive}
+ */
+ setStyle(style) {
+ if (!style || Object.keys(style).length === 0) {
+ return this
+ }
+ Util.merge(this._style, style)
+ return this
+ }
+
+ destroy() {
+ return Cesium.destroyObject(this)
+ }
+}
+
+Overlay.registerType('light-cylinder-primitive')
+
+export default LightCylinderPrimitive
diff --git a/src/modules/overlay/primitive/ModelCollectionPrimitive.js b/src/modules/overlay/primitive/ModelCollectionPrimitive.js
new file mode 100644
index 00000000..7320337a
--- /dev/null
+++ b/src/modules/overlay/primitive/ModelCollectionPrimitive.js
@@ -0,0 +1,132 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-08-02 20:12:04
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import State from '../../state/State'
+import Parse from '../../parse/Parse'
+import { Transform } from '../../transform'
+import { Util } from '../../utils'
+
+class ModelCollectionPrimitive extends Overlay {
+ constructor(positions, modelUrl) {
+ super()
+ this._positions = Parse.parsePositions(positions)
+ this._modelUrl = modelUrl
+ this._attrs = []
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('model_collection_primitive')
+ }
+
+ get readyPromise() {
+ return this._delegate.readyPromise
+ }
+
+ set attrs(attrs) {
+ this._attrs = attrs
+ }
+
+ get attrs() {
+ return this._attrs
+ }
+
+ set positions(positions) {
+ this._positions = Parse.parsePositions(positions)
+ if (this._layer) {
+ this._layer.delegate.remove(this._delegate)
+ this._resetDelegate()
+ this._layer.delegate.add(this._delegate)
+ }
+ }
+
+ set modelUrl(modelUrl) {
+ this._modelUrl = modelUrl
+ if (this._layer) {
+ this._layer.delegate.remove(this._delegate)
+ this._resetDelegate()
+ this._layer.delegate.add(this._delegate)
+ }
+ }
+
+ get modelUrl() {
+ return this._modelUrl
+ }
+
+ _resetDelegate() {
+ this._delegate = new Cesium.ModelInstanceCollection({
+ url: this._modelUrl,
+ instances: this._positions.map((item) => {
+ let origin = Transform.transformWGS84ToCartesian(item)
+ let modelMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(
+ origin,
+ new Cesium.HeadingPitchRoll(
+ Cesium.Math.toRadians(item.heading),
+ Cesium.Math.toRadians(item.pitch),
+ Cesium.Math.toRadians(item.roll)
+ )
+ )
+ this._style?.scale &&
+ Cesium.Matrix4.multiplyByUniformScale(
+ modelMatrix,
+ this._style?.scale,
+ modelMatrix
+ )
+ return {
+ modelMatrix,
+ }
+ }),
+ ...this._style,
+ })
+ this._delegate.layerId = this._layer?.layerId
+ this._delegate.overlayId = this._id
+ }
+
+ _mountedHook() {
+ this._resetDelegate()
+ }
+
+ /**
+ *
+ * @param instanceId
+ * @returns {undefined}
+ */
+ getModelInstance(instanceId) {
+ return this._delegate._instances[instanceId] || undefined
+ }
+
+ /**
+ *
+ * @param instanceId
+ * @returns {*|{}}
+ */
+ getAttrByInstanceId(instanceId) {
+ return this._attrs[instanceId] || {}
+ }
+
+ /**
+ *
+ * @param style
+ * @returns {ModelCollectionPrimitive}
+ */
+ setStyle(style) {
+ if (!style || Object.keys(style).length === 0) {
+ return this
+ }
+ delete style['instances'] && delete style['url']
+ Util.merge(this._style, style)
+ if (this._layer) {
+ this._resetDelegate()
+ this._layer.delegate.add(this._delegate)
+ }
+ return this
+ }
+}
+
+Overlay.registerType('model_collection_primitive')
+
+export default ModelCollectionPrimitive
diff --git a/src/modules/overlay/primitive/ModelPrimitive.js b/src/modules/overlay/primitive/ModelPrimitive.js
new file mode 100644
index 00000000..3d227d32
--- /dev/null
+++ b/src/modules/overlay/primitive/ModelPrimitive.js
@@ -0,0 +1,118 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-05-14 00:33:27
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import State from '../../state/State'
+import Parse from '../../parse/Parse'
+import { Transform } from '../../transform'
+import { Util } from '../../utils'
+
+class ModelPrimitive extends Overlay {
+ constructor(position, modelUrl) {
+ super()
+ this._position = Parse.parsePosition(position)
+ this._modelUrl = modelUrl
+ this._delegate = Cesium.Model.fromGltf({ url: modelUrl })
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('model_primitive')
+ }
+
+ get readyPromise() {
+ return this._delegate.readyPromise
+ }
+
+ set position(position) {
+ this._position = Parse.parsePosition(position)
+ let origin = Transform.transformWGS84ToCartesian(this._position)
+ this._delegate.modelMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(
+ origin,
+ new Cesium.HeadingPitchRoll(
+ Cesium.Math.toRadians(this._position.heading),
+ Cesium.Math.toRadians(this._position.pitch),
+ Cesium.Math.toRadians(this._position.roll)
+ )
+ )
+ }
+
+ get position() {
+ return this._position
+ }
+
+ set modelUrl(modelUrl) {
+ this._modelUrl = modelUrl
+ this._delegate = Cesium.Model.fromGltf({ url: modelUrl })
+ this.position = this._position
+ }
+
+ get modelUrl() {
+ return this._modelUrl
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountedHook() {
+ /**
+ * set the location
+ */
+ this.position = this._position
+ }
+
+ /**
+ *
+ * @param name
+ */
+ getMaterial(name) {
+ return this._delegate.getMaterial(name)
+ }
+
+ /**
+ *
+ * @param name
+ */
+ getMesh(name) {
+ return this._delegate.getMesh(name)
+ }
+
+ /**
+ *
+ * @param name
+ * @returns {*}
+ */
+ getNode(name) {
+ return this._delegate.getNode(name)
+ }
+
+ /**
+ *
+ * @returns {*}
+ */
+ getNodes() {
+ return this._delegate._runtime.nodes
+ }
+
+ /**
+ * Sets style
+ * @param style
+ * @returns {ModelPrimitive}
+ */
+ setStyle(style) {
+ if (!style || Object.keys(style).length === 0) {
+ return this
+ }
+ Util.merge(this._style, style)
+ Util.merge(this._delegate, style)
+ return this
+ }
+}
+
+Overlay.registerType('model_primitive')
+
+export default ModelPrimitive
diff --git a/src/modules/overlay/primitive/PointPrimitive.js b/src/modules/overlay/primitive/PointPrimitive.js
new file mode 100644
index 00000000..01edd947
--- /dev/null
+++ b/src/modules/overlay/primitive/PointPrimitive.js
@@ -0,0 +1,74 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-06-03 20:51:25
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import State from '../../state/State'
+import Parse from '../../parse/Parse'
+import { Transform } from '../../transform'
+import { Util } from '../../utils'
+
+const DEF_STYLE = {
+ pixelSize: 8,
+ outlineColor: Cesium.Color.BLUE,
+ outlineWidth: 2,
+}
+
+class PointPrimitive extends Overlay {
+ constructor(position) {
+ super()
+ this._position = Parse.parsePosition(position)
+
+ this._delegate = {
+ position: undefined,
+ }
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('point_primitive')
+ }
+
+ set position(position) {
+ this._position = Parse.parsePosition(position)
+ this._delegate.position = Transform.transformWGS84ToCartesian(
+ this._position
+ )
+ }
+
+ get position() {
+ return this._position
+ }
+
+ _mountedHook() {
+ /**
+ * set the location
+ */
+ this.position = this._position
+
+ /**
+ * initialize the Overlay parameter
+ */
+ Util.merge(this._delegate, DEF_STYLE, this._style)
+ }
+
+ /**
+ * Set style
+ * @param style
+ * @returns {PointPrimitive}
+ */
+ setStyle(style) {
+ if (!style || Object.keys(style).length === 0) {
+ return this
+ }
+ delete style['position']
+ Util.merge(this._style, style)
+ Util.merge(this._delegate, DEF_STYLE, this._style)
+ return this
+ }
+}
+Overlay.registerType('point_primitive')
+
+export default PointPrimitive
diff --git a/src/modules/overlay/primitive/PolylinePrimitive.js b/src/modules/overlay/primitive/PolylinePrimitive.js
new file mode 100644
index 00000000..f83e5a75
--- /dev/null
+++ b/src/modules/overlay/primitive/PolylinePrimitive.js
@@ -0,0 +1,71 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-06-03 21:16:27
+ */
+
+import Overlay from '../Overlay'
+import State from '../../state/State'
+import Parse from '../../parse/Parse'
+import { Transform } from '../../transform'
+import { Util } from '../../utils'
+import { center, distance } from '../../math'
+
+class PolylinePrimitive extends Overlay {
+ constructor(positions) {
+ super()
+ this._positions = Parse.parsePositions(positions)
+ this._delegate = {
+ positions: [],
+ }
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('polyline_primitive')
+ }
+
+ set positions(positions) {
+ this._positions = Parse.parsePositions(positions)
+ this._delegate.positions = Transform.transformWGS84ArrayToCartesianArray(
+ this._positions
+ )
+ }
+
+ get positions() {
+ return this._positions
+ }
+
+ get center() {
+ return center(this._positions)
+ }
+
+ get distance() {
+ return distance(this._positions)
+ }
+
+ _mountedHook() {
+ /**
+ * initialize the Overlay parameter
+ */
+ this.positions = this._positions
+ }
+
+ /**
+ * Sets style
+ * @param style
+ * @returns {PolylinePrimitive}
+ */
+ setStyle(style) {
+ if (!style || Object.keys(style).length === 0) {
+ return this
+ }
+ delete style['positions']
+ Util.merge(this._style, style)
+ Util.merge(this._delegate, style)
+ return this
+ }
+}
+
+Overlay.registerType('polyline_primitive')
+
+export default PolylinePrimitive
diff --git a/src/modules/overlay/primitive/ScanCirclePrimitive.js b/src/modules/overlay/primitive/ScanCirclePrimitive.js
new file mode 100644
index 00000000..4c0e7bf7
--- /dev/null
+++ b/src/modules/overlay/primitive/ScanCirclePrimitive.js
@@ -0,0 +1,100 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-12-31 11:05:32
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import State from '../../state/State'
+import Parse from '../../parse/Parse'
+import { Transform } from '../../transform'
+import { Util } from '../../utils'
+
+class ScanCirclePrimitive extends Overlay {
+ constructor(position, radius) {
+ super()
+ this._position = Parse.parsePosition(position)
+ this._radius = radius
+ this._delegate = new Cesium.GroundPrimitive({
+ geometryInstances: new Cesium.GeometryInstance({
+ geometry: {},
+ }),
+ })
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('scan_circle_primitive')
+ }
+
+ set position(position) {
+ this._position = Parse.parsePosition(position)
+ this._delegate.geometryInstances.geometry = new Cesium.EllipseGeometry({
+ center: Transform.transformWGS84ToCartesian(this._position),
+ semiMajorAxis: this._radius,
+ semiMinorAxis: this._radius,
+ })
+ }
+
+ get position() {
+ return this._position
+ }
+
+ set radius(radius) {
+ this._radius = radius
+ this._delegate.geometryInstances.geometry.semiMajorAxis = this._radius
+ this._delegate.geometryInstances.geometry.semiMinorAxis = this._radius
+ }
+
+ get radius() {
+ return this._radius
+ }
+
+ /**
+ *
+ * @private
+ */
+ _setAppearance() {
+ if (!this._style) {
+ return
+ }
+ this._delegate.appearance = new Cesium.MaterialAppearance({
+ material: Cesium.Material.fromType('CircleScan', {
+ color: this._style?.color || Cesium.Color.WHITE,
+ speed: this._style?.speed || 10,
+ }),
+ })
+ }
+
+ _mountedHook() {
+ /**
+ * set the position
+ */
+ this.position = this._position
+
+ /**
+ * set the appearance
+ */
+ !this._delegate.appearance && this._setAppearance()
+ }
+
+ /**
+ * Sets Style
+ * @param style
+ * @returns {ScanCirclePrimitive}
+ */
+ setStyle(style = {}) {
+ if (Object.keys(style).length === 0) {
+ return this
+ }
+ Util.merge(this._style, style)
+ style.classificationType &&
+ (this._delegate.classificationType = this._style.classificationType)
+ this._setAppearance()
+ return this
+ }
+}
+
+Overlay.registerType('scan_circle_primitive')
+
+export default ScanCirclePrimitive
diff --git a/src/modules/overlay/primitive/TrailLinePrimitive.js b/src/modules/overlay/primitive/TrailLinePrimitive.js
new file mode 100644
index 00000000..c2855bd6
--- /dev/null
+++ b/src/modules/overlay/primitive/TrailLinePrimitive.js
@@ -0,0 +1,85 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-01-09
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import State from '../../state/State'
+import Parse from '../../parse/Parse'
+import { Transform } from '../../transform'
+import { Util } from '../../utils'
+
+class TrailLinePrimitive extends Overlay {
+ constructor(positions, width = 1) {
+ super()
+ this._positions = Parse.parsePositions(positions)
+ this._width = width
+ this._delegate = new Cesium.Primitive({
+ geometryInstances: new Cesium.GeometryInstance({
+ geometry: {},
+ }),
+ })
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('trail_line_primitive')
+ }
+
+ set positions(positions) {
+ this._positions = Parse.parsePositions(positions)
+ this._delegate.geometryInstances.geometry = new Cesium.PolylineGeometry({
+ positions: Transform.transformWGS84ArrayToCartesianArray(this._positions),
+ width: this._width,
+ })
+ }
+
+ get positions() {
+ return this._positions
+ }
+
+ /**
+ *
+ * @private
+ */
+ _setAppearance() {
+ this._delegate.appearance = new Cesium.PolylineMaterialAppearance({
+ material: Cesium.Material.fromType('PolylineTrail', {
+ color: this._style?.color || new Cesium.Color(1.0, 0.0, 0.0, 0.7),
+ speed: this._style?.speed || 5,
+ }),
+ })
+ }
+
+ _mountedHook() {
+ /**
+ * set the positions
+ */
+ this.positions = this._positions
+ /**
+ * set the appearance
+ */
+ !this._delegate.appearance && this._setAppearance()
+ }
+
+ /**
+ * Sets Style
+ * @param style
+ * @returns {TrailLinePrimitive}
+ */
+ setStyle(style = {}) {
+ if (Object.keys(style).length === 0) {
+ return this
+ }
+ Util.merge(this._style, style)
+ style.classificationType &&
+ (this._delegate.classificationType = this._style.classificationType)
+ this._setAppearance()
+ return this
+ }
+}
+
+Overlay.registerType('trail_line_primitive')
+
+export default TrailLinePrimitive
diff --git a/src/modules/overlay/primitive/VideoPrimitive.js b/src/modules/overlay/primitive/VideoPrimitive.js
new file mode 100644
index 00000000..c3416d60
--- /dev/null
+++ b/src/modules/overlay/primitive/VideoPrimitive.js
@@ -0,0 +1,101 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-11-09 20:04:30
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import State from '../../state/State'
+import Parse from '../../parse/Parse'
+import { Transform } from '../../transform'
+import { Util } from '../../utils'
+
+class VideoPrimitive extends Overlay {
+ constructor(positions, video) {
+ super()
+ this._positions = Parse.parsePositions(positions)
+ this._delegate = new Cesium.GroundPrimitive({
+ geometryInstances: new Cesium.GeometryInstance({
+ geometry: {},
+ }),
+ })
+ this._video = video
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('video_primitive')
+ }
+
+ set positions(positions) {
+ this._positions = Parse.parsePositions(positions)
+ this._delegate.geometryInstances.geometry =
+ Cesium.PolygonGeometry.fromPositions({
+ positions: Transform.transformWGS84ArrayToCartesianArray(
+ this._positions
+ ),
+ height: this._style?.height,
+ extrudedHeight: this._style?.extrudedHeight,
+ closeTop: this._style?.closeTop,
+ closeBottom: this._style?.closeBottom,
+ vertexFormat: Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT,
+ })
+ }
+
+ get positions() {
+ return this._positions
+ }
+
+ set video(video) {
+ this._video = video
+ this._setAppearance()
+ }
+
+ get video() {
+ return this._video
+ }
+
+ /**
+ *
+ * @private
+ */
+ _setAppearance() {
+ this._delegate.appearance = new Cesium.EllipsoidSurfaceAppearance({
+ material: Cesium.Material.fromType('Image', {
+ image: this._video,
+ }),
+ })
+ }
+
+ _mountedHook() {
+ /**
+ * set the positions
+ */
+ this.positions = this._positions
+
+ /**
+ * initialize the Overlay parameter
+ */
+ this.video = this._video
+ }
+
+ /**
+ * Sets Style
+ * @param style
+ * @returns {VideoPrimitive}
+ */
+ setStyle(style) {
+ if (Object.keys(style).length === 0) {
+ return this
+ }
+ Util.merge(this._style, style)
+ if (this._style?.classificationType) {
+ this._delegate.classificationType = this._style.classificationType
+ }
+ return this
+ }
+}
+
+Overlay.registerType('video_primitive')
+
+export default VideoPrimitive
diff --git a/src/modules/overlay/primitive/WaterPrimitive.js b/src/modules/overlay/primitive/WaterPrimitive.js
new file mode 100644
index 00000000..9e602205
--- /dev/null
+++ b/src/modules/overlay/primitive/WaterPrimitive.js
@@ -0,0 +1,110 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-10-11 18:24:37
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import State from '../../state/State'
+import Parse from '../../parse/Parse'
+import { Transform } from '../../transform'
+import { Util } from '../../utils'
+
+class WaterPrimitive extends Overlay {
+ constructor(positions, holes = []) {
+ super()
+ this._positions = Parse.parsePositions(positions)
+ this._holes = holes.map((item) => Parse.parsePositions(item))
+ this._delegate = new Cesium.GroundPrimitive({
+ geometryInstances: new Cesium.GeometryInstance({
+ geometry: {},
+ }),
+ asynchronous: true,
+ })
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('water_primitive')
+ }
+
+ set positions(positions) {
+ this._positions = Parse.parsePositions(positions)
+ this._delegate.geometryInstances.geometry = new Cesium.PolygonGeometry({
+ polygonHierarchy: new Cesium.PolygonHierarchy(
+ Transform.transformWGS84ArrayToCartesianArray(this._positions),
+ this._holes.map(
+ (item) =>
+ new Cesium.PolygonHierarchy(
+ Transform.transformWGS84ArrayToCartesianArray(item)
+ )
+ )
+ ),
+ height: this._style?.height,
+ extrudedHeight: this._style?.extrudedHeight,
+ closeTop: this._style?.closeTop,
+ closeBottom: this._style?.closeBottom,
+ vertexFormat: Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT,
+ })
+ }
+
+ get positions() {
+ return this._positions
+ }
+
+ /**
+ *
+ * @private
+ */
+ _setAppearance() {
+ if (!this._style) {
+ return
+ }
+ this._delegate.appearance = new Cesium.EllipsoidSurfaceAppearance({
+ material: Cesium.Material.fromType('Water', {
+ baseWaterColor:
+ this._style?.baseWaterColor || new Cesium.Color(0.2, 0.3, 0.6, 1.0),
+ blendColor:
+ this._style?.blendColor || new Cesium.Color(0.0, 1.0, 0.699, 1.0),
+ specularMap: this._style?.specularMap || Cesium.Material.DefaultImageId,
+ normalMap: this._style?.normalMap || Cesium.Material.DefaultImageId,
+ frequency: this._style?.frequency || 1000.0,
+ animationSpeed: this._style?.animationSpeed || 0.01,
+ amplitude: this._style?.amplitude || 10,
+ specularIntensity: this._style?.specularIntensity || 0.5,
+ }),
+ })
+ }
+
+ _mountedHook() {
+ /**
+ * set the positions
+ */
+ this.positions = this._positions
+ /**
+ * set the appearance
+ */
+ !this._delegate.appearance && this._setAppearance()
+ }
+
+ /**
+ * Sets Style
+ * @param style
+ * @returns {WaterPrimitive}
+ */
+ setStyle(style) {
+ if (Object.keys(style).length === 0) {
+ return this
+ }
+ Util.merge(this._style, style)
+ if (this._style?.classificationType) {
+ this._delegate.classificationType = this._style.classificationType
+ }
+ this._setAppearance()
+ return this
+ }
+}
+
+Overlay.registerType('water_primitive')
+
+export default WaterPrimitive
diff --git a/src/modules/overlay/vector/Billboard.js b/src/modules/overlay/vector/Billboard.js
new file mode 100644
index 00000000..f104bffe
--- /dev/null
+++ b/src/modules/overlay/vector/Billboard.js
@@ -0,0 +1,113 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-19 10:18:23
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import State from '../../state/State'
+import Parse from '../../parse/Parse'
+import { Util } from '../../utils'
+import { Transform } from '../../transform'
+
+class Billboard extends Overlay {
+ constructor(position, icon) {
+ super()
+ this._delegate = new Cesium.Entity({ billboard: {} })
+ this._position = Parse.parsePosition(position)
+ this._icon = icon
+ this._size = [32, 32]
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('billboard')
+ }
+
+ set position(position) {
+ this._position = Parse.parsePosition(position)
+ this._delegate.position = Transform.transformWGS84ToCartesian(
+ this._position
+ )
+ return this
+ }
+
+ get position() {
+ return this._position
+ }
+
+ set icon(icon) {
+ this._icon = icon
+ this._delegate.billboard.image = this._icon
+ return this
+ }
+
+ get icon() {
+ return this._icon
+ }
+
+ set size(size) {
+ if (!Array.isArray(size)) {
+ throw new Error('Billboard: the size invalid')
+ }
+ this._size = size
+ this._delegate.billboard.width = this._size[0] || 32
+ this._delegate.billboard.height = this._size[1] || 32
+ return this
+ }
+
+ get size() {
+ return this._size
+ }
+
+ _mountedHook() {
+ /**
+ * set the location
+ */
+ this.position = this._position
+ /**
+ * initialize the Overlay parameter
+ */
+ this.icon = this._icon
+ this.size = this._size
+ }
+
+ /**
+ *
+ * @param style
+ * @returns {Billboard}
+ */
+ setStyle(style) {
+ if (!style || Object.keys(style).length === 0) {
+ return this
+ }
+ delete style['image'] && delete style['width'] && delete style['height']
+ Util.merge(this._style, style)
+ Util.merge(this._delegate.billboard, style)
+ return this
+ }
+
+ /**
+ * Parse from entity
+ * @param entity
+ * @returns {any}
+ */
+ static fromEntity(entity) {
+ let billboard = undefined
+ let now = Cesium.JulianDate.now()
+ let position = Transform.transformCartesianToWGS84(
+ entity.position.getValue(now)
+ )
+ if (entity.billboard) {
+ billboard = new Billboard(position, entity.billboard.image.getValue(now))
+ billboard.attr = {
+ ...entity?.properties?.getValue(now),
+ }
+ }
+ return billboard
+ }
+}
+
+Overlay.registerType('billboard')
+
+export default Billboard
diff --git a/src/modules/overlay/vector/Box.js b/src/modules/overlay/vector/Box.js
new file mode 100644
index 00000000..d14f919a
--- /dev/null
+++ b/src/modules/overlay/vector/Box.js
@@ -0,0 +1,116 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-02-25 18:28:36
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import State from '../../state/State'
+import Parse from '../../parse/Parse'
+import { Util } from '../../utils'
+import { Transform } from '../../transform'
+
+class Box extends Overlay {
+ constructor(position, length, width, height) {
+ super()
+ this._position = Parse.parsePosition(position)
+ this._length = length
+ this._width = width
+ this._height = height
+ this._delegate = new Cesium.Entity({
+ box: {
+ dimensions: {
+ x: +this._length,
+ y: +this._width,
+ z: +this._height,
+ },
+ },
+ })
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('box')
+ }
+
+ /**
+ *
+ * @param position
+ * @returns {Box}
+ */
+ set position(position) {
+ this._position = Parse.parsePosition(position)
+ this._delegate.position = Transform.transformWGS84ToCartesian(
+ this._position
+ )
+ this._delegate.orientation = Cesium.Transforms.headingPitchRollQuaternion(
+ Transform.transformWGS84ToCartesian(this._position),
+ new Cesium.HeadingPitchRoll(
+ Cesium.Math.toRadians(this._position.heading),
+ Cesium.Math.toRadians(this._position.pitch),
+ Cesium.Math.toRadians(this._position.roll)
+ )
+ )
+ return this
+ }
+
+ get position() {
+ return this._position
+ }
+
+ set length(length) {
+ this._length = length || 0
+ this._delegate.box.dimensions.x = +this._length
+ return this
+ }
+
+ get length() {
+ return this._length
+ }
+
+ set width(width) {
+ this._width = width || 0
+ this._delegate.box.dimensions.y = +this._width
+ return this
+ }
+
+ get width() {
+ return this._width
+ }
+
+ set height(height) {
+ this._height = height || 0
+ this._delegate.box.dimensions.z = +this._height
+ return this
+ }
+
+ get height() {
+ return this._height
+ }
+
+ _mountedHook() {
+ /**
+ * set the location
+ */
+ this.position = this._position
+ }
+
+ /**
+ * Sets Style
+ * @param style
+ * @returns {Box}
+ */
+ setStyle(style) {
+ if (Object.keys(style).length === 0) {
+ return this
+ }
+ delete style['length'] && delete style['width'] && delete style['height']
+ Util.merge(this._style, style)
+ Util.merge(this._delegate.box, style)
+ return this
+ }
+}
+
+Overlay.registerType('box')
+
+export default Box
diff --git a/src/modules/overlay/vector/Circle.js b/src/modules/overlay/vector/Circle.js
new file mode 100644
index 00000000..a95de417
--- /dev/null
+++ b/src/modules/overlay/vector/Circle.js
@@ -0,0 +1,127 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-31 18:57:02
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import State from '../../state/State'
+import Parse from '../../parse/Parse'
+import { Util } from '../../utils'
+import { Transform } from '../../transform'
+
+class Circle extends Overlay {
+ constructor(center, radius) {
+ super()
+ this._delegate = new Cesium.Entity({ polygon: {} })
+ this._center = Parse.parsePosition(center)
+ this._radius = +radius || 0
+ this._rotateAmount = 0
+ this._stRotation = 0
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('circle')
+ }
+
+ set center(center) {
+ this._center = Parse.parsePosition(center)
+ this._delegate.polygon.hierarchy = this._computeHierarchy()
+ return this
+ }
+
+ get center() {
+ return this._center
+ }
+
+ set radius(radius) {
+ this._radius = +radius
+ this._delegate.polygon.hierarchy = this._computeHierarchy()
+ return this
+ }
+
+ get radius() {
+ return this._radius
+ }
+
+ set rotateAmount(amount) {
+ this._rotateAmount = +amount
+ this._delegate.polygon.stRotation = new Cesium.CallbackProperty(() => {
+ this._stRotation += this._rotateAmount
+ if (this._stRotation >= 360 || this._stRotation <= -360) {
+ this._stRotation = 0
+ }
+ return Cesium.Math.toRadians(this._stRotation)
+ }, false)
+ return this
+ }
+
+ get rotateAmount() {
+ return this._rotateAmount
+ }
+
+ /**
+ *
+ * @private
+ */
+ _computeHierarchy() {
+ let result = new Cesium.PolygonHierarchy()
+ let cep = Cesium.EllipseGeometryLibrary.computeEllipsePositions(
+ {
+ center: Transform.transformWGS84ToCartesian(this._center),
+ semiMajorAxis: this._radius,
+ semiMinorAxis: this._radius,
+ rotation: 0,
+ granularity: 0.005,
+ },
+ false,
+ true
+ )
+ let pnts = Cesium.Cartesian3.unpackArray(cep.outerPositions)
+ pnts.push(pnts[0])
+ result.positions = pnts
+ return result
+ }
+
+ _mountedHook() {
+ /**
+ * set the location
+ */
+ this.center = this._center
+ }
+
+ /**
+ * Sets Text with Style
+ * @param text
+ * @param textStyle
+ * @returns {Circle}
+ */
+ setLabel(text, textStyle) {
+ this._delegate.position = Transform.transformWGS84ToCartesian(this._center)
+ this._delegate.label = {
+ ...textStyle,
+ text: text,
+ }
+ return this
+ }
+
+ /**
+ *
+ * @param style
+ * @returns {Circle}
+ */
+ setStyle(style) {
+ if (!style || Object.keys(style).length === 0) {
+ return this
+ }
+ delete style['positions']
+ Util.merge(this._style, style)
+ Util.merge(this._delegate.polygon, style)
+ return this
+ }
+}
+
+Overlay.registerType('circle')
+
+export default Circle
diff --git a/src/modules/overlay/vector/Corridor.js b/src/modules/overlay/vector/Corridor.js
new file mode 100644
index 00000000..e3e51a79
--- /dev/null
+++ b/src/modules/overlay/vector/Corridor.js
@@ -0,0 +1,90 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-04-11 18:58:17
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import State from '../../state/State'
+import Parse from '../../parse/Parse'
+import { Util } from '../../utils'
+import { Transform } from '../../transform'
+
+class Corridor extends Overlay {
+ constructor(positions) {
+ super()
+ this._positions = Parse.parsePositions(positions)
+ this._delegate = new Cesium.Entity({ corridor: {} })
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('corridor')
+ }
+
+ set positions(positions) {
+ this._positions = Parse.parsePositions(positions)
+ this._delegate.corridor.positions =
+ Transform.transformWGS84ArrayToCartesianArray(this._positions)
+ return this
+ }
+
+ get positions() {
+ return this._positions
+ }
+
+ _mountedHook() {
+ /**
+ * set the location
+ */
+ this.positions = this._positions
+ }
+
+ /**
+ *
+ * @param {*} text
+ * @param {*} textStyle
+ */
+ setLabel(text, textStyle) {
+ return this
+ }
+
+ /**
+ * Sets Style
+ * @param style
+ * @returns {Corridor}
+ */
+ setStyle(style) {
+ if (Object.keys(style).length === 0) {
+ return this
+ }
+ delete style['positions']
+ Util.merge(this._style, style)
+ Util.merge(this._delegate.corridor, style)
+ return this
+ }
+
+ /**
+ * Parses from entity
+ * @param entity
+ * @returns {Corridor|any}
+ */
+ static fromEntity(entity) {
+ let corridor = undefined
+ let now = Cesium.JulianDate.now()
+ if (entity.polyline) {
+ let positions = Transform.transformCartesianArrayToWGS84Array(
+ entity.polyline.positions.getValue(now)
+ )
+ corridor = new Corridor(positions)
+ corridor.attr = {
+ ...entity?.properties?.getValue(now),
+ }
+ }
+ return corridor
+ }
+}
+
+Overlay.registerType('corridor')
+
+export default Corridor
diff --git a/src/modules/overlay/vector/Cylinder.js b/src/modules/overlay/vector/Cylinder.js
new file mode 100644
index 00000000..7dd269e9
--- /dev/null
+++ b/src/modules/overlay/vector/Cylinder.js
@@ -0,0 +1,122 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-04-14 18:10:00
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import State from '../../state/State'
+import Parse from '../../parse/Parse'
+import { Util } from '../../utils'
+import { Transform } from '../../transform'
+
+class Cylinder extends Overlay {
+ constructor(position, length, topRadius, bottomRadius) {
+ super()
+ this._position = Parse.parsePosition(position)
+ this._length = +length || 0
+ this._topRadius = +topRadius || 0
+ this._bottomRadius = +bottomRadius || 0
+ this._delegate = new Cesium.Entity({ cylinder: {} })
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('cylinder')
+ }
+
+ set position(position) {
+ this._position = Parse.parsePosition(position)
+ this._delegate.position = Transform.transformWGS84ToCartesian(
+ this._position
+ )
+ this._delegate.orientation = Cesium.Transforms.headingPitchRollQuaternion(
+ Transform.transformWGS84ToCartesian(this._position),
+ new Cesium.HeadingPitchRoll(
+ Cesium.Math.toRadians(this._position.heading),
+ Cesium.Math.toRadians(this._position.pitch),
+ Cesium.Math.toRadians(this._position.roll)
+ )
+ )
+ return this
+ }
+
+ get position() {
+ return this._position
+ }
+
+ set length(length) {
+ this._length = +length || 0
+ this._delegate.cylinder.length = this._length
+ return this
+ }
+
+ get length() {
+ return this._length
+ }
+
+ set topRadius(topRadius) {
+ this._topRadius = +topRadius || 0
+ this._delegate.cylinder.topRadius = this._topRadius
+ return this
+ }
+
+ get topRadius() {
+ return this._topRadius
+ }
+
+ set bottomRadius(bottomRadius) {
+ this._bottomRadius = +bottomRadius || 0
+ this._delegate.cylinder.bottomRadius = this._bottomRadius
+ return this
+ }
+
+ get bottomRadius() {
+ return this._bottomRadius
+ }
+
+ _mountedHook() {
+ /**
+ * set the location
+ */
+ this.position = this._position
+ /**
+ * initialize the Overlay parameter
+ */
+ this.length = this._length
+ this.topRadius = this._topRadius
+ this.bottomRadius = this._bottomRadius
+ }
+
+ /**
+ *
+ * @param {*} text
+ * @param {*} textStyle
+ */
+ setLabel(text, textStyle) {
+ return this
+ }
+
+ /**
+ * Sets Style
+ * @param style
+ * @returns {Cylinder}
+ */
+ setStyle(style) {
+ if (Object.keys(style).length === 0) {
+ return this
+ }
+
+ delete style['length'] &&
+ delete style['topRadius'] &&
+ delete style['bottomRadius']
+
+ Util.merge(this._style, style)
+ Util.merge(this._delegate.cylinder, style)
+ return this
+ }
+}
+
+Overlay.registerType('cylinder')
+
+export default Cylinder
diff --git a/src/modules/overlay/vector/Ellipse.js b/src/modules/overlay/vector/Ellipse.js
new file mode 100644
index 00000000..506c4508
--- /dev/null
+++ b/src/modules/overlay/vector/Ellipse.js
@@ -0,0 +1,97 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-04-14 18:30:45
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import State from '../../state/State'
+import Parse from '../../parse/Parse'
+import { Util } from '../../utils'
+import { Transform } from '../../transform'
+
+class Ellipse extends Overlay {
+ constructor(position, semiMajorAxis, semiMinorAxis) {
+ super()
+ this._position = Parse.parsePosition(position)
+ this._semiMajorAxis = +semiMajorAxis || 0
+ this._semiMinorAxis = +semiMinorAxis || 0
+ this._delegate = new Cesium.Entity({ ellipse: {} })
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('ellipse')
+ }
+
+ set position(position) {
+ this._position = Parse.parsePosition(position)
+ this._delegate.position = Transform.transformWGS84ToCartesian(
+ this._position
+ )
+ this._delegate.orientation = Cesium.Transforms.headingPitchRollQuaternion(
+ Transform.transformWGS84ToCartesian(this._position),
+ new Cesium.HeadingPitchRoll(
+ Cesium.Math.toRadians(this._position.heading),
+ Cesium.Math.toRadians(this._position.pitch),
+ Cesium.Math.toRadians(this._position.roll)
+ )
+ )
+ return this
+ }
+
+ get position() {
+ return this._position
+ }
+
+ set semiMajorAxis(semiMajorAxis) {
+ this._semiMajorAxis = +semiMajorAxis || 0
+ this._delegate.ellipse.semiMajorAxis = this._semiMajorAxis
+ return this
+ }
+
+ get semiMajorAxis() {
+ return this._semiMajorAxis
+ }
+
+ set semiMinorAxis(semiMinorAxis) {
+ this._semiMinorAxis = +semiMinorAxis || 0
+ this._delegate.ellipse.semiMinorAxis = this._semiMinorAxis
+ return this
+ }
+
+ get semiMinorAxis() {
+ return this._semiMinorAxis
+ }
+
+ _mountedHook() {
+ /**
+ * set the location
+ */
+ this.position = this._position
+ /**
+ * initialize the Overlay parameter
+ */
+ this.semiMajorAxis = this._semiMajorAxis
+ this.semiMinorAxis = this._semiMinorAxis
+ }
+
+ /**
+ * Sets Style
+ * @param style
+ * @returns {Ellipse}
+ */
+ setStyle(style) {
+ if (Object.keys(style).length === 0) {
+ return this
+ }
+ delete style['semiMajorAxis'] && delete style['semiMinorAxis']
+ Util.merge(this._style, style)
+ Util.merge(this._delegate.ellipse, style)
+ return this
+ }
+}
+
+Overlay.registerType('ellipse')
+
+export default Ellipse
diff --git a/src/modules/overlay/vector/Ellipsoid.js b/src/modules/overlay/vector/Ellipsoid.js
new file mode 100644
index 00000000..da0bbf23
--- /dev/null
+++ b/src/modules/overlay/vector/Ellipsoid.js
@@ -0,0 +1,86 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-04-14 18:20:23
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import State from '../../state/State'
+import Parse from '../../parse/Parse'
+import { Util } from '../../utils'
+import { Transform } from '../../transform'
+
+class Ellipsoid extends Overlay {
+ constructor(position, radius) {
+ super()
+ this._position = Parse.parsePosition(position)
+ this._radius = radius || { x: 10, y: 10, z: 10 }
+ this._delegate = new Cesium.Entity({ ellipsoid: {} })
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('ellipsoid')
+ }
+
+ set position(position) {
+ this._position = Parse.parsePosition(position)
+ this._delegate.position = Transform.transformWGS84ToCartesian(
+ this._position
+ )
+ this._delegate.orientation = Cesium.Transforms.headingPitchRollQuaternion(
+ Transform.transformWGS84ToCartesian(this._position),
+ new Cesium.HeadingPitchRoll(
+ Cesium.Math.toRadians(this._position.heading),
+ Cesium.Math.toRadians(this._position.pitch),
+ Cesium.Math.toRadians(this._position.roll)
+ )
+ )
+ return this
+ }
+
+ get position() {
+ return this._position
+ }
+
+ set radius(radius) {
+ this._radius = radius || { x: 10, y: 10, z: 10 }
+ this._delegate.ellipsoid.radii = this._radius
+ return this
+ }
+
+ get radius() {
+ return this._radius
+ }
+
+ _mountedHook() {
+ /**
+ * set the location
+ */
+ this.position = this._position
+
+ /**
+ * initialize the Overlay parameter
+ */
+ this.radius = this._radius
+ }
+
+ /**
+ * Sets Style
+ * @param style
+ * @returns {Ellipsoid}
+ */
+ setStyle(style) {
+ if (Object.keys(style).length === 0) {
+ return this
+ }
+ delete style['radius']
+ Util.merge(this._style, style)
+ Util.merge(this._delegate.ellipsoid, style)
+ return this
+ }
+}
+
+Overlay.registerType('ellipsoid')
+
+export default Ellipsoid
diff --git a/src/modules/overlay/vector/Label.js b/src/modules/overlay/vector/Label.js
new file mode 100644
index 00000000..63532448
--- /dev/null
+++ b/src/modules/overlay/vector/Label.js
@@ -0,0 +1,107 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-02-01 11:59:28
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import State from '../../state/State'
+import Parse from '../../parse/Parse'
+import { Util } from '../../utils'
+import { Transform } from '../../transform'
+
+class Label extends Overlay {
+ constructor(position, text) {
+ super()
+ this._delegate = new Cesium.Entity({ label: {} })
+ this._position = Parse.parsePosition(position)
+ this._text = text
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('label')
+ }
+
+ set position(position) {
+ this._position = Parse.parsePosition(position)
+ this._delegate.position = Transform.transformWGS84ToCartesian(
+ this._position
+ )
+ return this
+ }
+
+ get position() {
+ return this._position
+ }
+
+ set text(text) {
+ this._text = text
+ this._delegate.label.text = this._text
+ return this
+ }
+
+ get text() {
+ return this._text
+ }
+
+ _mountedHook() {
+ /**
+ * set the location
+ */
+ this.position = this._position
+
+ /**
+ * initialize the Overlay parameter
+ */
+ this.text = this._text
+ }
+
+ /**
+ *
+ * @param {*} text
+ * @param {*} textStyle
+ */
+ setLabel(text, textStyle) {
+ return this
+ }
+
+ /**
+ * Sets Style
+ * @param style
+ * @returns {Label}
+ */
+ setStyle(style) {
+ if (!style || Object.keys(style).length === 0) {
+ return this
+ }
+ delete style['text']
+ Util.merge(this._style, style)
+ Util.merge(this._delegate.label, style)
+ return this
+ }
+
+ /**
+ * Parse from entity
+ * @param entity
+ * @returns {any}
+ */
+ static fromEntity(entity) {
+ let now = Cesium.JulianDate.now()
+ let position = Transform.transformCartesianToWGS84(
+ entity.position.getValue(now)
+ )
+ let label = undefined
+ if (entity.billboard) {
+ label = new Label(position, entity.name)
+ label.attr = {
+ ...entity?.properties?.getValue(now),
+ }
+ }
+ return label
+ }
+}
+
+Overlay.registerType('label')
+
+export default Label
diff --git a/src/modules/overlay/vector/Plane.js b/src/modules/overlay/vector/Plane.js
new file mode 100644
index 00000000..24e127b7
--- /dev/null
+++ b/src/modules/overlay/vector/Plane.js
@@ -0,0 +1,127 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-02-18 19:08:26
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import State from '../../state/State'
+import Parse from '../../parse/Parse'
+import { Util } from '../../utils'
+import { Transform } from '../../transform'
+
+class Plane extends Overlay {
+ constructor(position, width, height, plane = {}) {
+ super()
+ this._position = Parse.parsePosition(position)
+ this._width = +width || 0
+ this._height = +height || 0
+ if (plane.normal && typeof plane.normal === 'string') {
+ let n = String(plane.normal).toLocaleUpperCase()
+ plane.normal =
+ n === 'X'
+ ? Cesium.Cartesian3.UNIT_X
+ : n === 'Y'
+ ? Cesium.Cartesian3.UNIT_Y
+ : Cesium.Cartesian3.UNIT_Z
+ } else {
+ plane.normal = Cesium.Cartesian3.UNIT_Z
+ }
+ this._normal = plane.normal
+ this._distance = plane.distance || 0
+ this._delegate = new Cesium.Entity({
+ plane: {
+ dimensions: {
+ x: this._width,
+ y: this._height,
+ },
+ plane: new Cesium.Plane(this._normal, this._distance),
+ },
+ })
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('plane')
+ }
+
+ set position(position) {
+ this._position = Parse.parsePosition(position)
+ this._delegate.position = Transform.transformWGS84ToCartesian(
+ this._position
+ )
+ this._delegate.orientation = Cesium.Transforms.headingPitchRollQuaternion(
+ Transform.transformWGS84ToCartesian(this._position),
+ new Cesium.HeadingPitchRoll(
+ Cesium.Math.toRadians(this._position.heading),
+ Cesium.Math.toRadians(this._position.pitch),
+ Cesium.Math.toRadians(this._position.roll)
+ )
+ )
+ return this
+ }
+
+ get position() {
+ return this._position
+ }
+
+ set width(width) {
+ this._width = +width || 0
+ this._delegate.plan.dimensions.x = this._width
+ return this
+ }
+
+ get width() {
+ return this._width
+ }
+
+ set height(height) {
+ this._height = +height || 0
+ this._delegate.plan.dimensions.y = this._height
+ return this
+ }
+
+ get height() {
+ return this._height
+ }
+
+ set distance(distance) {
+ this._distance = distance
+ this._delegate.plane.plane.distance = distance
+ return this
+ }
+
+ get distance() {
+ return this._distance
+ }
+
+ _mountedHook() {
+ /**
+ * set the location
+ */
+ this.position = this._position
+ /**
+ * initialize the Overlay parameter
+ */
+ this.distance = this._distance
+ }
+
+ /**
+ * Sets Style
+ * @param style
+ * @returns {Plane}
+ */
+ setStyle(style) {
+ if (Object.keys(style).length === 0) {
+ return this
+ }
+ delete style['dimensions'] && delete ['plane']
+ Util.merge(this._style, style)
+ Util.merge(this._delegate.plane, style)
+ return this
+ }
+}
+
+Overlay.registerType('plane')
+
+export default Plane
diff --git a/src/modules/overlay/vector/Point.js b/src/modules/overlay/vector/Point.js
new file mode 100644
index 00000000..a7411611
--- /dev/null
+++ b/src/modules/overlay/vector/Point.js
@@ -0,0 +1,91 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-06 15:03:25
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import State from '../../state/State'
+import Parse from '../../parse/Parse'
+import { Util } from '../../utils'
+import { Transform } from '../../transform'
+
+const DEF_STYLE = {
+ pixelSize: 8,
+ outlineColor: Cesium.Color.BLUE,
+ outlineWidth: 2,
+}
+
+class Point extends Overlay {
+ constructor(position) {
+ super()
+ this._delegate = new Cesium.Entity({ point: {} })
+ this._position = Parse.parsePosition(position)
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('point')
+ }
+
+ set position(position) {
+ this._position = Parse.parsePosition(position)
+ this._delegate.position = Transform.transformWGS84ToCartesian(
+ this._position
+ )
+ return this
+ }
+
+ get position() {
+ return this._position
+ }
+
+ _mountedHook() {
+ /**
+ * set the location
+ */
+ this.position = this._position
+
+ /**
+ * initialize the Overlay parameter
+ */
+ Util.merge(this._delegate.point, DEF_STYLE, this._style)
+ }
+
+ /**
+ * Set style
+ * @param style
+ * @returns {Point}
+ */
+ setStyle(style) {
+ if (!style || Object.keys(style).length === 0) {
+ return this
+ }
+ delete style['position']
+ Util.merge(this._style, style)
+ Util.merge(this._delegate.point, DEF_STYLE, this._style)
+ return this
+ }
+
+ /**
+ * Parse from entity
+ * @param entity
+ * @returns {any}
+ */
+ static fromEntity(entity) {
+ let point = undefined
+ let now = Cesium.JulianDate.now()
+ let position = Transform.transformCartesianToWGS84(
+ entity.position.getValue(now)
+ )
+ point = new Point(position)
+ point.attr = {
+ ...entity?.properties?.getValue(now),
+ }
+ return point
+ }
+}
+
+Overlay.registerType('point')
+
+export default Point
diff --git a/src/modules/overlay/vector/Polygon.js b/src/modules/overlay/vector/Polygon.js
new file mode 100644
index 00000000..f467a139
--- /dev/null
+++ b/src/modules/overlay/vector/Polygon.js
@@ -0,0 +1,135 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-09 09:10:37
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import State from '../../state/State'
+import Parse from '../../parse/Parse'
+import { Util } from '../../utils'
+import { Transform } from '../../transform'
+import { center, area } from '../../math'
+
+class Polygon extends Overlay {
+ constructor(positions) {
+ super()
+ this._delegate = new Cesium.Entity({ polygon: {} })
+ this._positions = Parse.parsePositions(positions)
+ this._holes = []
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('polygon')
+ }
+
+ set positions(positions) {
+ this._positions = Parse.parsePositions(positions)
+ this._delegate.polygon.hierarchy = this._computeHierarchy()
+ return this
+ }
+
+ get positions() {
+ return this._positions
+ }
+
+ set holes(holes) {
+ if (holes && holes.length) {
+ this._holes = holes.map((item) => Parse.parsePositions(item))
+ this._delegate.polygon.hierarchy = this._computeHierarchy()
+ }
+ return this
+ }
+
+ get holes() {
+ return this._holes
+ }
+
+ get center() {
+ return center([...this._positions, this._positions[0]])
+ }
+
+ get area() {
+ return area(this._positions)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _computeHierarchy() {
+ let result = new Cesium.PolygonHierarchy()
+ result.positions = Transform.transformWGS84ArrayToCartesianArray(
+ this._positions
+ )
+ result.holes = this._holes.map(
+ (item) =>
+ new Cesium.PolygonHierarchy(
+ Transform.transformWGS84ArrayToCartesianArray(item)
+ )
+ )
+ return result
+ }
+
+ _mountedHook() {
+ /**
+ * initialize the Overlay parameter
+ */
+ this.positions = this._positions
+ }
+
+ /**
+ * Sets text
+ * @param text
+ * @param textStyle
+ * @returns {Polygon}
+ */
+ setLabel(text, textStyle) {
+ this._delegate.position = Transform.transformWGS84ToCartesian(this.center)
+ this._delegate.label = {
+ text: text,
+ ...textStyle,
+ }
+ return this
+ }
+
+ /**
+ * Sets style
+ * @param style
+ * @returns {Polygon}
+ */
+ setStyle(style) {
+ if (!style || Object.keys(style).length === 0) {
+ return this
+ }
+ delete style['positions']
+ Util.merge(this._style, style)
+ Util.merge(this._delegate.polygon, style)
+ return this
+ }
+
+ /**
+ * Parse from entity
+ * @param entity
+ * @returns {any}
+ */
+ static fromEntity(entity) {
+ let polygon = undefined
+ let now = Cesium.JulianDate.now()
+ if (entity.polygon) {
+ let positions = Transform.transformCartesianArrayToWGS84Array(
+ entity.polygon.hierarchy.getValue(now).positions
+ )
+ polygon = new Polygon(positions)
+ polygon.attr = {
+ ...entity?.properties?.getValue(now),
+ }
+ }
+ return polygon
+ }
+}
+
+Overlay.registerType('polygon')
+
+export default Polygon
diff --git a/src/modules/overlay/vector/Polyline.js b/src/modules/overlay/vector/Polyline.js
new file mode 100644
index 00000000..e89eea91
--- /dev/null
+++ b/src/modules/overlay/vector/Polyline.js
@@ -0,0 +1,105 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-06 15:03:25
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import State from '../../state/State'
+import Parse from '../../parse/Parse'
+import { Util } from '../../utils'
+import { Transform } from '../../transform'
+import { center, distance } from '../../math'
+
+class Polyline extends Overlay {
+ constructor(positions) {
+ super()
+ this._positions = Parse.parsePositions(positions)
+ this._delegate = new Cesium.Entity({ polyline: {} })
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('polyline')
+ }
+
+ set positions(positions) {
+ this._positions = Parse.parsePositions(positions)
+ this._delegate.polyline.positions =
+ Transform.transformWGS84ArrayToCartesianArray(this._positions)
+ return this
+ }
+
+ get positions() {
+ return this._positions
+ }
+
+ get center() {
+ return center(this._positions)
+ }
+
+ get distance() {
+ return distance(this._positions)
+ }
+
+ _mountedHook() {
+ /**
+ * initialize the Overlay parameter
+ */
+ this.positions = this._positions
+ }
+
+ /**
+ * Sets Text
+ * @param text
+ * @param textStyle
+ * @returns {Polyline}
+ */
+ setLabel(text, textStyle) {
+ this._delegate.position = Transform.transformWGS84ToCartesian(this.center)
+ this._delegate.label = {
+ text: text,
+ ...textStyle,
+ }
+ return this
+ }
+
+ /**
+ * Sets style
+ * @param style
+ * @returns {Polyline}
+ */
+ setStyle(style) {
+ if (!style || Object.keys(style).length === 0) {
+ return this
+ }
+ delete style['positions']
+ Util.merge(this._style, style)
+ Util.merge(this._delegate.polyline, style)
+ return this
+ }
+
+ /**
+ * Parse from entity
+ * @param entity
+ * @returns {Polyline}
+ */
+ static fromEntity(entity) {
+ let polyline = undefined
+ let now = Cesium.JulianDate.now()
+ if (entity.polyline) {
+ let positions = Transform.transformCartesianArrayToWGS84Array(
+ entity.polyline.positions.getValue(now)
+ )
+ polyline = new Polyline(positions)
+ polyline.attr = {
+ ...entity?.properties?.getValue(now),
+ }
+ }
+ return polyline
+ }
+}
+
+Overlay.registerType('polyline')
+
+export default Polyline
diff --git a/src/modules/overlay/vector/PolylineVolume.js b/src/modules/overlay/vector/PolylineVolume.js
new file mode 100644
index 00000000..2810356e
--- /dev/null
+++ b/src/modules/overlay/vector/PolylineVolume.js
@@ -0,0 +1,107 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-04-14 20:10:34
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import State from '../../state/State'
+import Parse from '../../parse/Parse'
+import { Util } from '../../utils'
+import { Transform } from '../../transform'
+
+class PolylineVolume extends Overlay {
+ constructor(positions, shape) {
+ super()
+ this._positions = Parse.parsePositions(positions)
+ this._shape = shape || []
+ this._delegate = new Cesium.Entity({ polylineVolume: {} })
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('polyline_volume')
+ }
+
+ set positions(positions) {
+ this._positions = Parse.parsePositions(positions)
+ this._delegate.polylineVolume.positions =
+ Transform.transformWGS84ArrayToCartesianArray(this._positions)
+ return this
+ }
+
+ get positions() {
+ return this._positions
+ }
+
+ set shape(shape) {
+ this._shape = shape || []
+ this._delegate.polylineVolume.shape = this._shape
+ return this
+ }
+
+ get shape() {
+ return this._shape
+ }
+
+ _mountedHook() {
+ /**
+ * set the location
+ */
+ this.positions = this._positions
+
+ /**
+ * initialize the Overlay parameter
+ */
+ this.shape = this._shape
+ }
+
+ /**
+ * @param text
+ * @param textStyle
+ * @returns {PolylineVolume}
+ */
+ setLabel(text, textStyle) {
+ return this
+ }
+
+ /**
+ * Sets style
+ * @param style
+ * @returns {PolylineVolume}
+ */
+ setStyle(style) {
+ if (Object.keys(style).length === 0) {
+ return this
+ }
+ delete style['positions'] && delete style['shape']
+ Util.merge(this._style, style)
+ Util.merge(this._delegate.polylineVolume, style)
+ return this
+ }
+
+ /**
+ * Parses from entity
+ * @param entity
+ * @param shape
+ * @returns {PolylineVolume|any}
+ */
+ static fromEntity(entity, shape) {
+ let polylineVolume = undefined
+ let now = Cesium.JulianDate.now()
+ if (entity.polyline) {
+ let positions = Transform.transformCartesianArrayToWGS84Array(
+ entity.polyline.positions.getValue(now)
+ )
+ polylineVolume = new PolylineVolume(positions, shape)
+ polylineVolume.attr = {
+ ...entity?.properties?.getValue(now),
+ }
+ }
+ return polylineVolume
+ }
+}
+
+Overlay.registerType('polyline_volume')
+
+export default PolylineVolume
diff --git a/src/modules/overlay/vector/Rectangle.js b/src/modules/overlay/vector/Rectangle.js
new file mode 100644
index 00000000..590655df
--- /dev/null
+++ b/src/modules/overlay/vector/Rectangle.js
@@ -0,0 +1,81 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-04-14 20:46:23
+ */
+
+import { Cesium } from '../../../namespace'
+import Overlay from '../Overlay'
+import State from '../../state/State'
+import Parse from '../../parse/Parse'
+import { Util } from '../../utils'
+import { Transform } from '../../transform'
+
+class Rectangle extends Overlay {
+ constructor(positions) {
+ super()
+ this._positions = Parse.parsePositions(positions)
+ this._delegate = new Cesium.Entity({ rectangle: {} })
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('rectangle')
+ }
+
+ set positions(positions) {
+ this._positions = Parse.parsePositions(positions)
+ this._delegate.rectangle.coordinates = Cesium.Rectangle.fromCartesianArray(
+ Transform.transformWGS84ArrayToCartesianArray(this._positions)
+ )
+ return this
+ }
+
+ get positions() {
+ return this._positions
+ }
+
+ _mountedHook() {
+ /**
+ * set the location
+ */
+ this.positions = this._positions
+ }
+
+ /**
+ * @param text
+ * @param textStyle
+ * @returns {Rectangle}
+ */
+ setLabel(text, textStyle) {
+ this._delegate.position = Cesium.Cartographic.toCartesian(
+ Cesium.Rectangle.center(
+ this._delegate.rectangle,
+ new Cesium.Cartographic()
+ )
+ )
+ this._delegate.label = {
+ ...textStyle,
+ text: text,
+ }
+ return this
+ }
+
+ /**
+ * Sets Style
+ * @param style
+ * @returns {Rectangle}
+ */
+ setStyle(style) {
+ if (Object.keys(style).length === 0) {
+ return this
+ }
+ delete style['positions']
+ Util.merge(this._style, style)
+ Util.merge(this._delegate.rectangle, style)
+ return this
+ }
+}
+
+Overlay.registerType('rectangle')
+
+export default Rectangle
diff --git a/src/modules/overlay/vector/Wall.js b/src/modules/overlay/vector/Wall.js
new file mode 100644
index 00000000..0e8436bd
--- /dev/null
+++ b/src/modules/overlay/vector/Wall.js
@@ -0,0 +1,91 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-02-25 18:28:36
+ */
+
+import { Cesium } from '../../../namespace'
+import State from '../../state/State'
+import Parse from '../../parse/Parse'
+import { Util } from '../../utils'
+import { Transform } from '../../transform'
+import Overlay from '../Overlay'
+
+class Wall extends Overlay {
+ constructor(positions) {
+ super()
+ this._positions = Parse.parsePositions(positions)
+ this._delegate = new Cesium.Entity({ wall: {} })
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Overlay.getOverlayType('wall')
+ }
+
+ set positions(positions) {
+ this._positions = Parse.parsePositions(positions)
+ this._delegate.wall.positions =
+ Transform.transformWGS84ArrayToCartesianArray(this._positions)
+ return this
+ }
+
+ get positions() {
+ return this._positions
+ }
+
+ _mountedHook() {
+ /**
+ * set the location
+ */
+ this.positions = this._positions
+ }
+
+ /**
+ *
+ * @param text
+ * @param textStyle
+ * @returns {Wall}
+ */
+ setLabel(text, textStyle) {
+ return this
+ }
+
+ /**
+ * Sets Style
+ * @param style
+ * @returns {Wall}
+ */
+ setStyle(style) {
+ if (Object.keys(style).length === 0) {
+ return this
+ }
+ delete style['positions']
+ Util.merge(this._style, style)
+ Util.merge(this._delegate.wall, style)
+ return this
+ }
+
+ /**
+ * Parses from entity
+ * @param entity
+ * @returns {Wall|any}
+ */
+ static fromEntity(entity) {
+ let wall = undefined
+ let now = Cesium.JulianDate.now()
+ if (entity.polyline) {
+ let positions = Transform.transformCartesianArrayToWGS84Array(
+ entity.polyline.positions.getValue(now)
+ )
+ wall = new Wall(positions)
+ wall.attr = {
+ ...entity?.properties?.getValue(now),
+ }
+ }
+ return wall
+ }
+}
+
+Overlay.registerType('wall')
+
+export default Wall
diff --git a/src/modules/parse/Parse.js b/src/modules/parse/Parse.js
new file mode 100644
index 00000000..0ca7f83b
--- /dev/null
+++ b/src/modules/parse/Parse.js
@@ -0,0 +1,106 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-03-22 00:10:25
+ */
+import Position from '../position/Position'
+
+class Parse {
+ /**
+ * Parses all kinds of coordinates to position
+ * @param position
+ * @returns {Position}
+ */
+ static parsePosition(position) {
+ let result = new Position()
+ if (!position) {
+ return result
+ }
+ if (typeof position === 'string') {
+ result = Position.fromString(position)
+ } else if (Array.isArray(position)) {
+ result = Position.fromArray(position)
+ } else if (
+ !(Object(position) instanceof Position) &&
+ Object(position).hasOwnProperty('lng') &&
+ Object(position).hasOwnProperty('lat')
+ ) {
+ result = Position.fromObject(position)
+ } else if (Object(position) instanceof Position) {
+ result = position
+ }
+ return result
+ }
+
+ /**
+ * Parses all kinds of coordinates array to position array
+ * @param positions
+ * @returns {unknown[]}
+ */
+ static parsePositions(positions) {
+ if (typeof positions === 'string') {
+ if (positions.indexOf('#') >= 0) {
+ throw new Error('the positions invalid')
+ }
+ positions = positions.split(';').filter((item) => !!item)
+ }
+ return positions.map((item) => {
+ if (typeof item === 'string') {
+ return Position.fromString(item)
+ } else if (Array.isArray(item)) {
+ return Position.fromArray(item)
+ } else if (
+ !(Object(item) instanceof Position) &&
+ Object(item).hasOwnProperty('lng') &&
+ Object(item).hasOwnProperty('lat')
+ ) {
+ return Position.fromObject(item)
+ } else if (Object(item) instanceof Position) {
+ return item
+ }
+ })
+ }
+
+ /**
+ * Parses point position to array
+ * @param position
+ * @returns {*[]}
+ */
+ static parsePointCoordToArray(position) {
+ position = this.parsePosition(position)
+ return [position.lng, position.lat]
+ }
+
+ /**
+ * Parses polyline positions to array
+ * @param positions
+ * @returns {[]}
+ */
+ static parsePolylineCoordToArray(positions) {
+ let result = []
+ positions = this.parsePositions(positions)
+ positions.forEach((item) => {
+ result.push([item.lng, item.lat])
+ })
+ return result
+ }
+
+ /**
+ * Parses polygon positions to array
+ * @param positions
+ * @param loop
+ * @returns {[][]}
+ */
+ static parsePolygonCoordToArray(positions, loop = false) {
+ let result = []
+ positions = this.parsePositions(positions)
+ positions.forEach((item) => {
+ result.push([item.lng, item.lat])
+ })
+ if (loop && result.length > 0) {
+ result.push(result[0])
+ }
+ return [result]
+ }
+}
+
+export default Parse
diff --git a/src/modules/plot/Plot.js b/src/modules/plot/Plot.js
new file mode 100644
index 00000000..049ff33c
--- /dev/null
+++ b/src/modules/plot/Plot.js
@@ -0,0 +1,217 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-29 19:26:06
+ */
+
+import { Cesium } from '../../namespace'
+import { OverlayType } from '../overlay'
+
+import DrawPoint from './draw/DrawPoint'
+import DrawPolyline from './draw/DrawPolyline'
+import DrawPolygon from './draw/DrawPolygon'
+import DrawCircle from './draw/DrawCircle'
+import DrawRectangle from './draw/DrawRectangle'
+import DrawBillboard from './draw/DrawBillboard'
+import DrawAttackArrow from './draw/DrawAttackArrow'
+import DrawDoubleArrow from './draw/DrawDoubleArrow'
+import DrawFineArrow from './draw/DrawFineArrow'
+import DrawGatheringPlace from './draw/DrawGatheringPlace'
+import DrawTailedAttackArrow from './draw/DrawTailedAttackArrow'
+
+import EditPoint from './edit/EditPoint'
+import EditPolyline from './edit/EditPolyline'
+import EditPolygon from './edit/EditPolygon'
+import EditCircle from './edit/EditCircle'
+import EditRectangle from './edit/EditRectangle'
+import EditBillboard from './edit/EditBillboard'
+import EditAttackArrow from './edit/EditAttackArrow'
+import EditDoubleArrow from './edit/EditDoubleArrow'
+import EditFineArrow from './edit/EditFineArrow'
+import EditGatheringPlace from './edit/EditGatheringPlace'
+import EditTailedAttackArrow from './edit/EditTailedAttackArrow'
+
+class Plot {
+ constructor(viewer, options = {}) {
+ this._viewer = viewer
+ this._options = options
+ this._layer = new Cesium.CustomDataSource('plot-layer')
+ this._viewer.dataSources.add(this._layer)
+ this._currentWorker = undefined
+ this._state = undefined
+ }
+
+ get viewer() {
+ return this._viewer
+ }
+
+ get layer() {
+ return this._layer
+ }
+
+ get state() {
+ return this._state
+ }
+
+ /**
+ *
+ * @param type
+ * @param style
+ * @private
+ */
+ _createDrawWorker(type, style) {
+ let drawWorker = undefined
+ switch (type) {
+ case OverlayType.POINT:
+ drawWorker = new DrawPoint(style)
+ break
+ case OverlayType.POLYLINE:
+ drawWorker = new DrawPolyline(style)
+ break
+ case OverlayType.POLYGON:
+ drawWorker = new DrawPolygon(style)
+ break
+ case OverlayType.CIRCLE:
+ drawWorker = new DrawCircle(style)
+ break
+ case OverlayType.RECTANGLE:
+ drawWorker = new DrawRectangle(style)
+ break
+ case OverlayType.BILLBOARD:
+ drawWorker = new DrawBillboard(style)
+ break
+ case OverlayType.ATTACK_ARROW:
+ drawWorker = new DrawAttackArrow(style)
+ break
+ case OverlayType.DOUBLE_ARROW:
+ drawWorker = new DrawDoubleArrow(style)
+ break
+ case OverlayType.FINE_ARROW:
+ drawWorker = new DrawFineArrow(style)
+ break
+ case OverlayType.TAILED_ATTACK_ARROW:
+ drawWorker = new DrawTailedAttackArrow(style)
+ break
+ case OverlayType.GATHERING_PLACE:
+ drawWorker = new DrawGatheringPlace(style)
+ break
+ default:
+ break
+ }
+ return drawWorker
+ }
+
+ /**
+ *
+ * @param overlay
+ * @private
+ */
+ _createEditWorker(overlay) {
+ let editWorker = undefined
+ switch (overlay.type) {
+ case OverlayType.POINT:
+ editWorker = new EditPoint(overlay)
+ break
+ case OverlayType.POLYLINE:
+ editWorker = new EditPolyline(overlay)
+ break
+ case OverlayType.POLYGON:
+ editWorker = new EditPolygon(overlay)
+ break
+ case OverlayType.CIRCLE:
+ editWorker = new EditCircle(overlay)
+ break
+ case OverlayType.RECTANGLE:
+ editWorker = new EditRectangle(overlay)
+ break
+ case OverlayType.BILLBOARD:
+ editWorker = new EditBillboard(overlay)
+ break
+ case OverlayType.ATTACK_ARROW:
+ editWorker = new EditAttackArrow(overlay)
+ break
+ case OverlayType.DOUBLE_ARROW:
+ editWorker = new EditDoubleArrow(overlay)
+ break
+ case OverlayType.FINE_ARROW:
+ editWorker = new EditFineArrow(overlay)
+ break
+ case OverlayType.TAILED_ATTACK_ARROW:
+ editWorker = new EditTailedAttackArrow(overlay)
+ break
+ case OverlayType.GATHERING_PLACE:
+ editWorker = new EditGatheringPlace(overlay)
+ break
+ default:
+ break
+ }
+ return editWorker
+ }
+
+ /**
+ *
+ * @param type
+ * @param callback
+ * @param style
+ * @param clampToModel
+ * @returns {Plot}
+ */
+ draw(type, callback, style = {}, clampToModel = false) {
+ this._state = 'draw'
+ if (this._currentWorker) {
+ this._currentWorker.stop()
+ }
+ let maxAnchorSize = style?.maxAnchorSize || 999
+ style && delete style['maxAnchorSize']
+ this._currentWorker = this._createDrawWorker(type, style)?.start(this, {
+ ...this._options,
+ maxAnchorSize: maxAnchorSize,
+ onDrawStop: callback,
+ clampToModel: clampToModel ?? this._options.clampToModel,
+ })
+ return this
+ }
+
+ /**
+ *
+ * @param overlay
+ * @param callback
+ * @param clampToModel
+ * @returns {Plot}
+ */
+ edit(overlay, callback, clampToModel = false) {
+ this._state = 'edit'
+ if (this._currentWorker) {
+ this._currentWorker.stop()
+ }
+ this._currentWorker = this._createEditWorker(overlay)?.start(this, {
+ ...this._options,
+ onEditStop: callback,
+ clampToModel: clampToModel ?? this._options.clampToModel,
+ })
+ return this
+ }
+
+ /**
+ *
+ * @return {Plot}
+ */
+ stop() {
+ if (this._currentWorker) {
+ this._currentWorker.stop()
+ }
+ this._currentWorker = null
+ return undefined
+ }
+
+ /**
+ *
+ * @returns {Plot}
+ */
+ destroy() {
+ this._viewer.dataSources.remove(this._layer)
+ this._viewer = undefined
+ return this
+ }
+}
+
+export default Plot
diff --git a/src/modules/plot/draw/Draw.js b/src/modules/plot/draw/Draw.js
new file mode 100644
index 00000000..de9f402c
--- /dev/null
+++ b/src/modules/plot/draw/Draw.js
@@ -0,0 +1,111 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-31 19:45:32
+ */
+import { PlotEventType } from '../../event'
+
+class Draw {
+ constructor(style) {
+ this._style = style
+ this._viewer = undefined
+ this._layer = undefined
+ this._delegate = undefined
+ this._options = {}
+ this._positions = []
+ }
+
+ get drawTool() {
+ return this._viewer.drawTool
+ }
+
+ /**
+ * The hook for mount viewer
+ * Subclasses need to be overridden
+ * @private
+ */
+ _mountedHook() {}
+
+ /**
+ * The hook for mount stop
+ * Subclasses need to be overridden
+ * @private
+ */
+ _stoppedHook() {}
+
+ /**
+ *
+ * @param position
+ * @private
+ */
+ _onDrawAnchor(position) {}
+
+ /**
+ *
+ * @param position
+ * @private
+ */
+ _onAnchorMoving(position) {
+ this._positions.pop()
+ this._positions.push(position)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _onDrawStop() {
+ this._unbindEvent()
+ this._viewer.drawTool.deactivate()
+ this._delegate && this._layer.entities.remove(this._delegate)
+ this._stoppedHook()
+ }
+
+ /**
+ *
+ * @private
+ */
+ _bindEvent() {
+ this.drawTool.on(PlotEventType.DRAW_ANCHOR, this._onDrawAnchor, this)
+ this.drawTool.on(PlotEventType.ANCHOR_MOVING, this._onAnchorMoving, this)
+ this.drawTool.on(PlotEventType.DRAW_STOP, this._onDrawStop, this)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _unbindEvent() {
+ this.drawTool.off(PlotEventType.DRAW_ANCHOR, this._onDrawAnchor, this)
+ this.drawTool.off(PlotEventType.ANCHOR_MOVING, this._onAnchorMoving, this)
+ this.drawTool.off(PlotEventType.DRAW_STOP, this._onDrawStop, this)
+ }
+
+ /**
+ *
+ * @param plot
+ * @param options
+ * @returns {Draw}
+ */
+ start(plot, options) {
+ this._viewer = plot.viewer
+ this._layer = plot.layer
+ this._options = options
+ this._viewer.editTool.deactivate()
+ this._viewer.drawTool.activate(options)
+ this._mountedHook()
+ this._unbindEvent()
+ this._bindEvent()
+ return this
+ }
+
+ /**
+ *
+ * @returns {Draw}
+ */
+ stop() {
+ this.drawTool.fire(PlotEventType.DRAW_STOP)
+ return this
+ }
+}
+
+export default Draw
diff --git a/src/modules/plot/draw/DrawAttackArrow.js b/src/modules/plot/draw/DrawAttackArrow.js
new file mode 100644
index 00000000..ceb5eea1
--- /dev/null
+++ b/src/modules/plot/draw/DrawAttackArrow.js
@@ -0,0 +1,82 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-30 16:43:12
+ */
+
+import { Cesium } from '../../../namespace'
+import Draw from './Draw'
+import { PlotEventType } from '../../event'
+import { Transform } from '../../transform'
+import { AttackArrow } from '../../overlay'
+import AttackArrowGraphics from '../graphics/AttackArrowGraphics'
+
+const DEF_STYLE = {
+ material: Cesium.Color.YELLOW.withAlpha(0.6),
+ fill: true,
+}
+
+class DrawAttackArrow extends Draw {
+ constructor(style) {
+ super()
+ this._maxAnchorSize = 3
+ this._style = {
+ ...DEF_STYLE,
+ ...style,
+ }
+ this._graphics = new AttackArrowGraphics()
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountedHook() {
+ this.drawTool.tooltipMess = '单击选择点位'
+ this._delegate = new Cesium.Entity({
+ polygon: {
+ ...this._style,
+ hierarchy: new Cesium.CallbackProperty(() => {
+ if (this._positions.length > 2) {
+ this._graphics.positions = this._positions
+ return this._graphics.hierarchy
+ } else {
+ return null
+ }
+ }, false),
+ },
+ })
+ this._layer.entities.add(this._delegate)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _stoppedHook() {
+ let attackArrow = null
+ if (this._positions.length) {
+ attackArrow = new AttackArrow(
+ Transform.transformCartesianArrayToWGS84Array(this._positions)
+ ).setStyle(this._style)
+ }
+ this._options.onDrawStop && this._options.onDrawStop(attackArrow)
+ }
+
+ /**
+ *
+ * @param position
+ * @private
+ */
+ _onDrawAnchor(position) {
+ let len = this._positions.length
+ this._positions.push(position)
+ this.drawTool.fire(PlotEventType.CREATE_ANCHOR, { position })
+ this._graphics.positions = this._positions
+ if (len >= this._maxAnchorSize) {
+ this._positions.pop()
+ this.drawTool.fire(PlotEventType.DRAW_STOP)
+ }
+ }
+}
+
+export default DrawAttackArrow
diff --git a/src/modules/plot/draw/DrawBillboard.js b/src/modules/plot/draw/DrawBillboard.js
new file mode 100644
index 00000000..9afb4b5d
--- /dev/null
+++ b/src/modules/plot/draw/DrawBillboard.js
@@ -0,0 +1,75 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-29 20:29:59
+ */
+
+import { Cesium } from '../../../namespace'
+import Draw from './Draw'
+import { PlotEventType } from '../../event'
+import { Transform } from '../../transform'
+import { Billboard } from '../../overlay'
+import IMG_CIRCLE_RED from '../../images/circle_red.png'
+
+class DrawPoint extends Draw {
+ constructor(style) {
+ super()
+ this._position = Cesium.Cartesian3.ZERO
+ this._style = {
+ image: IMG_CIRCLE_RED,
+ ...style,
+ }
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountedHook() {
+ this.drawTool.tooltipMess = '单击选择点位'
+ this._delegate = new Cesium.Entity({
+ position: new Cesium.CallbackProperty(() => {
+ return this._position
+ }, false),
+ billboard: {
+ ...this._style,
+ },
+ })
+ this._layer.entities.add(this._delegate)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _stoppedHook() {
+ let billboard = null
+ if (this._position) {
+ billboard = new Billboard(
+ Transform.transformCartesianToWGS84(this._position),
+ this._style.image
+ ).setStyle(this._style)
+ }
+ this._options.onDrawStop && this._options.onDrawStop(billboard)
+ }
+
+ /**
+ *
+ * @param position
+ * @private
+ */
+ _onDrawAnchor(position) {
+ this._position = position
+ this.drawTool.fire(PlotEventType.DRAW_STOP)
+ }
+
+ /**
+ *
+ * @param position
+ * @private
+ */
+ _onAnchorMoving(position) {
+ this._position = position
+ }
+}
+
+export default DrawPoint
diff --git a/src/modules/plot/draw/DrawCircle.js b/src/modules/plot/draw/DrawCircle.js
new file mode 100644
index 00000000..0fef138a
--- /dev/null
+++ b/src/modules/plot/draw/DrawCircle.js
@@ -0,0 +1,103 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-29 21:24:55
+ */
+
+import { Cesium } from '../../../namespace'
+import { PlotEventType } from '../../event'
+import { Transform } from '../../transform'
+import { Circle } from '../../overlay'
+import Draw from './Draw'
+
+const DEF_STYLE = {
+ material: Cesium.Color.YELLOW.withAlpha(0.6),
+ fill: true,
+}
+
+class DrawCircle extends Draw {
+ constructor(style) {
+ super()
+ this._maxAnchorSize = 2
+ this._radius = 0
+ this._style = {
+ ...DEF_STYLE,
+ ...style,
+ }
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountedHook() {
+ this.drawTool.tooltipMess = '单击选择点位'
+ this._delegate = new Cesium.Entity({
+ polygon: {
+ ...this._style,
+ hierarchy: new Cesium.CallbackProperty(() => {
+ if (this._positions.length > 1) {
+ this._radius = Cesium.Cartesian3.distance(
+ this._positions[0],
+ this._positions[1]
+ )
+ if (this._radius <= 0) {
+ return null
+ }
+ let cep = Cesium.EllipseGeometryLibrary.computeEllipsePositions(
+ {
+ center: this._positions[0],
+ semiMajorAxis: this._radius,
+ semiMinorAxis: this._radius,
+ rotation: 0,
+ granularity: 0.005,
+ },
+ false,
+ true
+ )
+ let pnts = Cesium.Cartesian3.unpackArray(cep.outerPositions)
+ pnts.push(pnts[0])
+ return new Cesium.PolygonHierarchy(pnts)
+ } else {
+ return null
+ }
+ }, false),
+ },
+ })
+ this._layer.entities.add(this._delegate)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _stoppedHook() {
+ let circle = null
+ if (this._positions.length) {
+ circle = new Circle(
+ Transform.transformCartesianToWGS84(this._positions[0]),
+ this._radius
+ ).setStyle(this._style)
+ }
+ this._options.onDrawStop && this._options.onDrawStop(circle)
+ }
+
+ /**
+ *
+ * @param position
+ * @private
+ */
+ _onDrawAnchor(position) {
+ let len = this._positions.length
+ this._positions.push(position)
+ this.drawTool.fire(PlotEventType.CREATE_ANCHOR, {
+ position,
+ isCenter: len === 1,
+ })
+ if (len >= this._maxAnchorSize) {
+ this._positions.pop()
+ this.drawTool.fire(PlotEventType.DRAW_STOP)
+ }
+ }
+}
+
+export default DrawCircle
diff --git a/src/modules/plot/draw/DrawDoubleArrow.js b/src/modules/plot/draw/DrawDoubleArrow.js
new file mode 100644
index 00000000..54827793
--- /dev/null
+++ b/src/modules/plot/draw/DrawDoubleArrow.js
@@ -0,0 +1,82 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-30
+ */
+
+import { Cesium } from '../../../namespace'
+import { PlotEventType } from '../../event'
+import { Transform } from '../../transform'
+import { DoubleArrow } from '../../overlay'
+import Draw from './Draw'
+import DoubleArrowGraphics from '../graphics/DoubleArrowGraphics'
+
+const DEF_STYLE = {
+ material: Cesium.Color.YELLOW.withAlpha(0.6),
+ fill: true,
+}
+
+class DrawDoubleArrow extends Draw {
+ constructor(style) {
+ super()
+ this._maxAnchorSize = 4
+ this._style = {
+ ...DEF_STYLE,
+ ...style,
+ }
+ this._graphics = new DoubleArrowGraphics()
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountedHook() {
+ this.drawTool.tooltipMess = '单击选择点位'
+ this._delegate = new Cesium.Entity({
+ polygon: {
+ ...this._style,
+ hierarchy: new Cesium.CallbackProperty(() => {
+ if (this._positions.length > 2) {
+ this._graphics.positions = this._positions
+ return this._graphics.hierarchy
+ } else {
+ return null
+ }
+ }, false),
+ },
+ })
+ this._layer.entities.add(this._delegate)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _stoppedHook() {
+ let doubleArrow = null
+ if (this._positions.length) {
+ doubleArrow = new DoubleArrow(
+ Transform.transformCartesianArrayToWGS84Array(this._positions)
+ ).setStyle(this._style)
+ }
+ this._options.onDrawStop && this._options.onDrawStop(doubleArrow)
+ }
+
+ /**
+ *
+ * @param position
+ * @private
+ */
+ _onDrawAnchor(position) {
+ let len = this._positions.length
+ this._positions.push(position)
+ this.drawTool.fire(PlotEventType.CREATE_ANCHOR, { position })
+ this._graphics.positions = this._positions
+ if (len >= this._maxAnchorSize) {
+ this._positions.pop()
+ this.drawTool.fire(PlotEventType.DRAW_STOP)
+ }
+ }
+}
+
+export default DrawDoubleArrow
diff --git a/src/modules/plot/draw/DrawFineArrow.js b/src/modules/plot/draw/DrawFineArrow.js
new file mode 100644
index 00000000..b5f450ce
--- /dev/null
+++ b/src/modules/plot/draw/DrawFineArrow.js
@@ -0,0 +1,83 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-30 16:43:12
+ */
+
+import { Cesium } from '../../../namespace'
+import Draw from './Draw'
+import { Transform } from '../../transform'
+import { PlotEventType } from '../../event'
+import { FineArrow } from '../../overlay'
+
+import FineArrowGraphics from '../graphics/FineArrowGraphics'
+
+const DEF_STYLE = {
+ material: Cesium.Color.YELLOW.withAlpha(0.6),
+ fill: true,
+}
+
+class DrawFineArrow extends Draw {
+ constructor(style) {
+ super()
+ this._maxAnchorSize = 2
+ this._style = {
+ ...DEF_STYLE,
+ ...style,
+ }
+ this._graphics = new FineArrowGraphics()
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountedHook() {
+ this.drawTool.tooltipMess = '单击选择点位'
+ this._delegate = new Cesium.Entity({
+ polygon: {
+ ...this._style,
+ hierarchy: new Cesium.CallbackProperty(() => {
+ if (this._positions.length > 1) {
+ this._graphics.positions = this._positions
+ return this._graphics.hierarchy
+ } else {
+ return null
+ }
+ }, false),
+ },
+ })
+ this._layer.entities.add(this._delegate)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _stoppedHook() {
+ let fineArrow = null
+ if (this._positions.length) {
+ fineArrow = new FineArrow(
+ Transform.transformCartesianArrayToWGS84Array(this._positions)
+ ).setStyle(this._style)
+ }
+ this._options.onDrawStop && this._options.onDrawStop(fineArrow)
+ }
+
+ /**
+ *
+ * @param position
+ * @private
+ */
+ _onDrawAnchor(position) {
+ let len = this._positions.length
+ this._positions.push(position)
+ this.drawTool.fire(PlotEventType.CREATE_ANCHOR, { position })
+ this._graphics.positions = this._positions
+ if (len >= this._maxAnchorSize) {
+ this._positions.pop()
+ this.drawTool.fire(PlotEventType.DRAW_STOP)
+ }
+ }
+}
+
+export default DrawFineArrow
diff --git a/src/modules/plot/draw/DrawGatheringPlace.js b/src/modules/plot/draw/DrawGatheringPlace.js
new file mode 100644
index 00000000..8a6bcaa6
--- /dev/null
+++ b/src/modules/plot/draw/DrawGatheringPlace.js
@@ -0,0 +1,83 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-30 17:22:21
+ */
+
+import { Cesium } from '../../../namespace'
+import Draw from './Draw'
+import { Transform } from '../../transform'
+import { PlotEventType } from '../../event'
+import { GatheringPlace } from '../../overlay'
+
+import GatheringPlaceGraphics from '../graphics/GatheringPlaceGraphics'
+
+const DEF_STYLE = {
+ material: Cesium.Color.YELLOW.withAlpha(0.6),
+ fill: true,
+}
+
+class DrawGatheringPlace extends Draw {
+ constructor(style) {
+ super()
+ this._maxAnchorSize = 3
+ this._style = {
+ ...DEF_STYLE,
+ ...style,
+ }
+ this._graphics = new GatheringPlaceGraphics()
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountedHook() {
+ this.drawTool.tooltipMess = '单击选择点位'
+ this._delegate = new Cesium.Entity({
+ polygon: {
+ ...this._style,
+ hierarchy: new Cesium.CallbackProperty(() => {
+ if (this._positions.length > 1) {
+ this._graphics.positions = this._positions
+ return this._graphics.hierarchy
+ } else {
+ return null
+ }
+ }, false),
+ },
+ })
+ this._layer.entities.add(this._delegate)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _stoppedHook() {
+ let gatheringPlace = null
+ if (this._positions.length) {
+ gatheringPlace = new GatheringPlace(
+ Transform.transformCartesianArrayToWGS84Array(this._positions)
+ ).setStyle(this._style)
+ }
+ this._options.onDrawStop && this._options.onDrawStop(gatheringPlace)
+ }
+
+ /**
+ *
+ * @param position
+ * @private
+ */
+ _onDrawAnchor(position) {
+ let len = this._positions.length
+ this._positions.push(position)
+ this.drawTool.fire(PlotEventType.CREATE_ANCHOR, { position })
+ this._graphics.positions = this._positions
+ if (len >= this._maxAnchorSize) {
+ this._positions.pop()
+ this.drawTool.fire(PlotEventType.DRAW_STOP)
+ }
+ }
+}
+
+export default DrawGatheringPlace
diff --git a/src/modules/plot/draw/DrawPoint.js b/src/modules/plot/draw/DrawPoint.js
new file mode 100644
index 00000000..f4d3612f
--- /dev/null
+++ b/src/modules/plot/draw/DrawPoint.js
@@ -0,0 +1,79 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-31 16:25:29
+ */
+
+import { Cesium } from '../../../namespace'
+import { PlotEventType } from '../../event'
+import { Transform } from '../../transform'
+import { Point } from '../../overlay'
+import Draw from './Draw'
+
+const DEF_STYLE = {
+ pixelSize: 10,
+ outlineColor: Cesium.Color.BLUE,
+ outlineWidth: 5,
+}
+
+class DrawPoint extends Draw {
+ constructor(style) {
+ super()
+ this._position = Cesium.Cartesian3.ZERO
+ this._style = {
+ ...DEF_STYLE,
+ ...style,
+ }
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountedHook() {
+ this.drawTool.tooltipMess = '单击选择点位'
+ this._delegate = new Cesium.Entity({
+ position: new Cesium.CallbackProperty(() => {
+ return this._position
+ }, false),
+ point: {
+ ...this._style,
+ },
+ })
+ this._layer.entities.add(this._delegate)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _stoppedHook() {
+ let point = null
+ if (this._position) {
+ point = new Point(
+ Transform.transformCartesianToWGS84(this._position)
+ ).setStyle(this._style)
+ }
+ this._options.onDrawStop && this._options.onDrawStop(point)
+ }
+
+ /**
+ *
+ * @param position
+ * @private
+ */
+ _onDrawAnchor(position) {
+ this._position = position
+ this.drawTool.fire(PlotEventType.DRAW_STOP, position)
+ }
+
+ /**
+ *
+ * @param position
+ * @private
+ */
+ _onAnchorMoving(position) {
+ this._position = position
+ }
+}
+
+export default DrawPoint
diff --git a/src/modules/plot/draw/DrawPolygon.js b/src/modules/plot/draw/DrawPolygon.js
new file mode 100644
index 00000000..2a9ddf61
--- /dev/null
+++ b/src/modules/plot/draw/DrawPolygon.js
@@ -0,0 +1,74 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-29 20:55:14
+ */
+
+import { Cesium } from '../../../namespace'
+import { PlotEventType } from '../../event'
+import { Transform } from '../../transform'
+import { Polygon } from '../../overlay'
+import Draw from './Draw'
+
+const DEF_STYLE = {
+ material: Cesium.Color.YELLOW.withAlpha(0.6),
+ fill: true,
+}
+
+class DrawPolygon extends Draw {
+ constructor(style) {
+ super()
+ this._style = {
+ ...DEF_STYLE,
+ ...style,
+ }
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountedHook() {
+ this.drawTool.tooltipMess = '左击选择点位,右击结束'
+ this._delegate = new Cesium.Entity({
+ polygon: {
+ ...this._style,
+ hierarchy: new Cesium.CallbackProperty(() => {
+ if (this._positions.length > 2) {
+ return new Cesium.PolygonHierarchy(
+ this._positions.map((item) => item.clone())
+ )
+ } else {
+ return null
+ }
+ }, false),
+ },
+ })
+ this._layer.entities.add(this._delegate)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _stoppedHook() {
+ let polygon = null
+ if (this._positions.length) {
+ polygon = new Polygon(
+ Transform.transformCartesianArrayToWGS84Array(this._positions)
+ ).setStyle(this._style)
+ }
+ this._options.onDrawStop && this._options.onDrawStop(polygon)
+ }
+
+ /**
+ *
+ * @param position
+ * @private
+ */
+ _onDrawAnchor(position) {
+ this._positions.push(position)
+ this.drawTool.fire(PlotEventType.CREATE_ANCHOR, { position })
+ }
+}
+
+export default DrawPolygon
diff --git a/src/modules/plot/draw/DrawPolyline.js b/src/modules/plot/draw/DrawPolyline.js
new file mode 100644
index 00000000..81848bfd
--- /dev/null
+++ b/src/modules/plot/draw/DrawPolyline.js
@@ -0,0 +1,74 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-29 20:54:37
+ */
+
+import { Cesium } from '../../../namespace'
+import { PlotEventType } from '../../event'
+import { Transform } from '../../transform'
+import { Polyline } from '../../overlay'
+import Draw from './Draw'
+
+const DEF_STYLE = {
+ width: 3,
+ material: Cesium.Color.YELLOW.withAlpha(0.6),
+}
+
+class DrawPolyline extends Draw {
+ constructor(style) {
+ super()
+ this._style = {
+ ...DEF_STYLE,
+ ...style,
+ }
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountedHook() {
+ this.drawTool.tooltipMess = '左击选择点位,右击结束'
+ this._delegate = new Cesium.Entity({
+ polyline: {
+ ...this._style,
+ positions: new Cesium.CallbackProperty(() => {
+ return this._positions
+ }, false),
+ },
+ })
+ this._layer.entities.add(this._delegate)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _stoppedHook() {
+ let polyline = null
+ if (this._positions.length) {
+ polyline = new Polyline(
+ Transform.transformCartesianArrayToWGS84Array(this._positions)
+ ).setStyle(this._style)
+ }
+ this._options.onDrawStop && this._options.onDrawStop(polyline)
+ }
+
+ /**
+ *
+ * @param position
+ * @returns {boolean}
+ * @private
+ */
+ _onDrawAnchor(position) {
+ let len = this._positions.length
+ this._positions.push(position)
+ this.drawTool.fire(PlotEventType.CREATE_ANCHOR, { position })
+ if (len >= this._options.maxAnchorSize) {
+ this._positions.pop()
+ this.drawTool.fire(PlotEventType.DRAW_STOP)
+ }
+ }
+}
+
+export default DrawPolyline
diff --git a/src/modules/plot/draw/DrawRectangle.js b/src/modules/plot/draw/DrawRectangle.js
new file mode 100644
index 00000000..0d860421
--- /dev/null
+++ b/src/modules/plot/draw/DrawRectangle.js
@@ -0,0 +1,77 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-29 21:30:41
+ */
+
+import { Cesium } from '../../../namespace'
+import Draw from './Draw'
+import { PlotEventType } from '../../event'
+import { Transform } from '../../transform'
+import { Rectangle } from '../../overlay'
+
+const DEF_STYLE = {
+ material: Cesium.Color.YELLOW.withAlpha(0.6),
+}
+
+class DrawRectangle extends Draw {
+ constructor(style) {
+ super()
+ this._maxAnchorSize = 2
+ this._style = {
+ ...DEF_STYLE,
+ ...style,
+ }
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountedHook() {
+ this.drawTool.tooltipMess = '左击选择点位'
+ this._delegate = new Cesium.Entity({
+ rectangle: {
+ ...this._style,
+ coordinates: new Cesium.CallbackProperty((time) => {
+ if (this._positions.length > 1) {
+ return Cesium.Rectangle.fromCartesianArray(this._positions)
+ } else {
+ return null
+ }
+ }, false),
+ },
+ })
+ this._layer.entities.add(this._delegate)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _stoppedHook() {
+ let rectangle = null
+ if (this._positions.length) {
+ rectangle = new Rectangle(
+ Transform.transformCartesianArrayToWGS84Array(this._positions)
+ ).setStyle(this._style)
+ }
+ this._options.onDrawStop && this._options.onDrawStop(rectangle)
+ }
+
+ /**
+ *
+ * @param position
+ * @private
+ */
+ _onDrawAnchor(position) {
+ let len = this._positions.length
+ this._positions.push(position)
+ this.drawTool.fire(PlotEventType.CREATE_ANCHOR, { position })
+ if (len >= this._maxAnchorSize) {
+ this._positions.pop()
+ this.drawTool.fire(PlotEventType.DRAW_STOP)
+ }
+ }
+}
+
+export default DrawRectangle
diff --git a/src/modules/plot/draw/DrawTailedAttackArrow.js b/src/modules/plot/draw/DrawTailedAttackArrow.js
new file mode 100644
index 00000000..db50c1b0
--- /dev/null
+++ b/src/modules/plot/draw/DrawTailedAttackArrow.js
@@ -0,0 +1,83 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-30 16:43:12
+ */
+
+import { Cesium } from '../../../namespace'
+import Draw from './Draw'
+import TailedAttackArrowGraphics from '../graphics/TailedAttackArrowGraphics'
+import { PlotEventType } from '../../event'
+import { Transform } from '../../transform'
+import { TailedAttackArrow } from '../../overlay'
+
+const DEF_STYLE = {
+ material: Cesium.Color.YELLOW.withAlpha(0.6),
+ fill: true,
+}
+
+class DrawTailedAttackArrow extends Draw {
+ constructor(style) {
+ super()
+ this._maxAnchorSize = 3
+ this._style = {
+ ...DEF_STYLE,
+ ...style,
+ }
+ this._graphics = new TailedAttackArrowGraphics()
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountEntity() {
+ this.drawTool.tooltipMess = '左击选择点位'
+ this._delegate = new Cesium.Entity({
+ polygon: {
+ ...this._style,
+ hierarchy: new Cesium.CallbackProperty(() => {
+ if (this._positions.length > 2) {
+ this._graphics.positions = this._positions
+ return this._graphics.hierarchy
+ } else {
+ return null
+ }
+ }, false),
+ },
+ })
+ this._layer.entities.add(this._delegate)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _stoppedHook() {
+ let tailedAttackArrow = null
+ if (this._positions.length) {
+ tailedAttackArrow = new TailedAttackArrow(
+ Transform.transformCartesianArrayToWGS84Array(this._positions)
+ ).setStyle(this._style)
+ }
+ this._options.onDrawStop && this._options.onDrawStop(tailedAttackArrow)
+ }
+
+ /**
+ *
+ * @param position
+ * @returns {boolean}
+ * @private
+ */
+ _onDrawAnchor(position) {
+ let len = this._positions.length
+ this._positions.push(position)
+ this.drawTool.fire(PlotEventType.CREATE_ANCHOR, { position })
+ this._graphics.positions = this._positions
+ if (len >= this._maxAnchorSize) {
+ this._positions.pop()
+ this.drawTool.fire(PlotEventType.DRAW_STOP)
+ }
+ }
+}
+
+export default DrawTailedAttackArrow
diff --git a/src/modules/plot/edit/Edit.js b/src/modules/plot/edit/Edit.js
new file mode 100644
index 00000000..e6313444
--- /dev/null
+++ b/src/modules/plot/edit/Edit.js
@@ -0,0 +1,159 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-30 23:50:53
+ */
+
+import { Cesium } from '../../../namespace'
+import { PlotEventType } from '../../event'
+import { Transform } from '../../transform'
+
+class Edit {
+ constructor(overlay) {
+ this._viewer = undefined
+ this._layer = undefined
+ this._overlay = overlay
+ this._overlay.show = false
+ this._delegate = new Cesium.Entity()
+ this._delegate.merge(overlay.delegate)
+ this._options = {}
+ this._positions = []
+ }
+
+ get editTool() {
+ return this._viewer.editTool
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountedHook() {}
+
+ /**
+ *
+ * @private
+ */
+ _stopedHook() {
+ this._overlay.positions = Transform.transformCartesianArrayToWGS84Array(
+ this._positions
+ )
+ this._overlay.show = true
+ this._options.onEditStop && this._options.onEditStop(this._overlay)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountAnchor() {
+ this._positions = [].concat(
+ Transform.transformWGS84ArrayToCartesianArray(this._overlay.positions)
+ )
+ this._positions.forEach((item, index) => {
+ this.editTool.fire(PlotEventType.CREATE_ANCHOR, {
+ position: item,
+ index: index,
+ })
+ })
+ }
+
+ /**
+ *
+ * @param pickedAnchor
+ * @param position
+ * @returns {boolean}
+ * @private
+ */
+ _onEditAnchorStop({ pickedAnchor, position }) {
+ let properties = pickedAnchor.properties.getValue(Cesium.JulianDate.now())
+ this._positions[properties.index] = position
+ }
+
+ /**
+ *
+ * @param pickedAnchor
+ * @param position
+ * @private
+ */
+ _onAnchorMoving({ pickedAnchor, position }) {
+ let properties = pickedAnchor.properties.getValue(Cesium.JulianDate.now())
+ this._positions[properties.index] = position
+ }
+
+ /**
+ *
+ * @param pickedAnchor
+ * @param position
+ * @private
+ */
+ _onEditStop({ pickedAnchor, position }) {
+ this._unbindEvent()
+ this._viewer.editTool.deactivate()
+ this._layer.entities.remove(this._delegate)
+ this._stopedHook()
+ }
+
+ /**
+ *
+ * @returns {Edit}
+ * @private
+ */
+ _bindEvent() {
+ this.editTool.on(PlotEventType.ANCHOR_MOVING, this._onAnchorMoving, this)
+ this.editTool.on(
+ PlotEventType.EDIT_ANCHOR_STOP,
+ this._onEditAnchorStop,
+ this
+ )
+ this.editTool.on(PlotEventType.EDIT_STOP, this._onEditStop, this)
+ return this
+ }
+
+ /**
+ *
+ * @private
+ */
+ _unbindEvent() {
+ this.editTool.off(PlotEventType.ANCHOR_MOVING, this._onAnchorMoving, this)
+ this.editTool.off(
+ PlotEventType.EDIT_ANCHOR_STOP,
+ this._onEditAnchorStop,
+ this
+ )
+ this.editTool.off(PlotEventType.EDIT_STOP, this._onEditStop, this)
+ }
+
+ /**
+ *
+ * @param plot
+ * @param options
+ * @returns {Edit}
+ */
+ start(plot, options) {
+ this._viewer = plot.viewer
+ this._layer = plot.layer
+ this._options = options
+ this._viewer.editTool.deactivate()
+ this._viewer.editTool.tooltipMess = '点击锚点移动,右击结束编辑'
+ this._viewer.editTool.activate(options)
+ this._mountedHook()
+ this._mountAnchor()
+ this._unbindEvent()
+ this._bindEvent()
+ return this
+ }
+
+ /**
+ *
+ * @returns {Edit}
+ */
+ stop() {
+ this.editTool.fire(PlotEventType.EDIT_STOP, {
+ pickedAnchor: null,
+ position: null,
+ })
+ return this
+ }
+}
+
+export default Edit
diff --git a/src/modules/plot/edit/EditAttackArrow.js b/src/modules/plot/edit/EditAttackArrow.js
new file mode 100644
index 00000000..647f5e73
--- /dev/null
+++ b/src/modules/plot/edit/EditAttackArrow.js
@@ -0,0 +1,33 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-30 23:46:07
+ */
+
+import { Cesium } from '../../../namespace'
+import Edit from './Edit'
+import AttackArrowGraphics from '../graphics/AttackArrowGraphics'
+
+class EditAttackArrow extends Edit {
+ constructor(overlay) {
+ super(overlay)
+ this._graphics = new AttackArrowGraphics()
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountedHook() {
+ this._delegate.polygon.hierarchy = new Cesium.CallbackProperty(() => {
+ if (this._positions.length > 2) {
+ this._graphics.positions = this._positions
+ return this._graphics.hierarchy
+ } else {
+ return null
+ }
+ }, false)
+ this._layer.entities.add(this._delegate)
+ }
+}
+
+export default EditAttackArrow
diff --git a/src/modules/plot/edit/EditBillboard.js b/src/modules/plot/edit/EditBillboard.js
new file mode 100644
index 00000000..d66eea28
--- /dev/null
+++ b/src/modules/plot/edit/EditBillboard.js
@@ -0,0 +1,50 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-30 22:04:36
+ */
+
+import { Cesium } from '../../../namespace'
+import Edit from './Edit'
+import { Transform } from '../../transform'
+
+class EditBillboard extends Edit {
+ constructor(overlay) {
+ super(overlay)
+ this._position = undefined
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountedHook() {
+ this.editTool.tooltipMess = '右击结束编辑'
+ this._position = this._delegate.position.getValue(Cesium.JulianDate.now())
+ this._delegate.position = new Cesium.CallbackProperty(() => {
+ return this._position
+ }, false)
+ this._layer.entities.add(this._delegate)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _stopedHook() {
+ this._overlay.position = Transform.transformCartesianToWGS84(this._position)
+ this._overlay.show = true
+ this._options.onEditStop && this._options.onEditStop(this._overlay)
+ }
+
+ /**
+ *
+ * @param pickedAnchor
+ * @param position
+ * @private
+ */
+ _onAnchorMoving({ pickedAnchor, position }) {
+ this._position = position
+ }
+}
+
+export default EditBillboard
diff --git a/src/modules/plot/edit/EditCircle.js b/src/modules/plot/edit/EditCircle.js
new file mode 100644
index 00000000..419f4dbe
--- /dev/null
+++ b/src/modules/plot/edit/EditCircle.js
@@ -0,0 +1,102 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-31 10:54:38
+ */
+
+import { Cesium } from '../../../namespace'
+import Edit from './Edit'
+import { PlotEventType } from '../../event'
+import { Transform } from '../../transform'
+
+class EditCircle extends Edit {
+ constructor(overlay) {
+ super(overlay)
+ this._center = undefined
+ this._radius = 0
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountedHook() {
+ this._radius = this._overlay.radius
+ this._center = Transform.transformWGS84ToCartesian(this._overlay.center)
+ this._positions = [].concat([
+ this._center,
+ this._computeCirclePoints(this._center, this._radius)[0],
+ ])
+ this._delegate.polygon.hierarchy = new Cesium.CallbackProperty((time) => {
+ if (this._positions.length > 1) {
+ this._radius = Cesium.Cartesian3.distance(
+ this._positions[0],
+ this._positions[1]
+ )
+ if (this._radius <= 0) {
+ return null
+ }
+ let pnts = this._computeCirclePoints(this._positions[0], this._radius)
+ pnts.push(pnts[0])
+ return new Cesium.PolygonHierarchy(pnts)
+ } else {
+ return null
+ }
+ }, false)
+ this._layer.entities.add(this._delegate)
+ }
+
+ /**
+ *
+ * @param center
+ * @param radius
+ * @returns {*[]}
+ * @private
+ */
+ _computeCirclePoints(center, radius) {
+ let pnts = []
+ let cep = Cesium.EllipseGeometryLibrary.computeEllipsePositions(
+ {
+ center: center,
+ semiMajorAxis: radius,
+ semiMinorAxis: radius,
+ rotation: 0,
+ granularity: 0.005,
+ },
+ false,
+ true
+ )
+ if (cep && cep.outerPositions) {
+ pnts = Cesium.Cartesian3.unpackArray(cep.outerPositions)
+ }
+ return pnts
+ }
+
+ /**
+ *
+ * @private
+ */
+ _stopedHook() {
+ this._overlay.center = Transform.transformCartesianToWGS84(
+ this._positions[0]
+ )
+ this._overlay.radius = this._radius
+ this._overlay.show = true
+ this._options.onEditStop && this._options.onEditStop(this._overlay)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountAnchor() {
+ this._positions.forEach((item, index) => {
+ this.editTool.fire(PlotEventType.CREATE_ANCHOR, {
+ position: item,
+ index: index,
+ isCenter: index % 2 === 0,
+ })
+ })
+ }
+}
+
+export default EditCircle
diff --git a/src/modules/plot/edit/EditDoubleArrow.js b/src/modules/plot/edit/EditDoubleArrow.js
new file mode 100644
index 00000000..d9468744
--- /dev/null
+++ b/src/modules/plot/edit/EditDoubleArrow.js
@@ -0,0 +1,33 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-30 23:46:07
+ */
+
+import { Cesium } from '../../../namespace'
+import Edit from './Edit'
+import DoubleArrowGraphics from '../graphics/DoubleArrowGraphics'
+
+class EditDoubleArrow extends Edit {
+ constructor(overlay) {
+ super(overlay)
+ this._graphics = new DoubleArrowGraphics()
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountedHook() {
+ this._delegate.polygon.hierarchy = new Cesium.CallbackProperty(() => {
+ if (this._positions.length > 2) {
+ this._graphics.positions = this._positions
+ return this._graphics.hierarchy
+ } else {
+ return null
+ }
+ }, false)
+ this._layer.entities.add(this._delegate)
+ }
+}
+
+export default EditDoubleArrow
diff --git a/src/modules/plot/edit/EditFineArrow.js b/src/modules/plot/edit/EditFineArrow.js
new file mode 100644
index 00000000..e3fa7071
--- /dev/null
+++ b/src/modules/plot/edit/EditFineArrow.js
@@ -0,0 +1,33 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-30 23:46:07
+ */
+
+import { Cesium } from '../../../namespace'
+import Edit from './Edit'
+import FineArrowGraphics from '../graphics/FineArrowGraphics'
+
+class EditFineArrow extends Edit {
+ constructor(overlay) {
+ super(overlay)
+ this._graphics = new FineArrowGraphics()
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountedHook() {
+ this._delegate.polygon.hierarchy = new Cesium.CallbackProperty(() => {
+ if (this._positions.length > 1) {
+ this._graphics.positions = this._positions
+ return this._graphics.hierarchy
+ } else {
+ return null
+ }
+ }, false)
+ this._layer.entities.add(this._delegate)
+ }
+}
+
+export default EditFineArrow
diff --git a/src/modules/plot/edit/EditGatheringPlace.js b/src/modules/plot/edit/EditGatheringPlace.js
new file mode 100644
index 00000000..44ab9500
--- /dev/null
+++ b/src/modules/plot/edit/EditGatheringPlace.js
@@ -0,0 +1,33 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-30 23:46:07
+ */
+
+import { Cesium } from '../../../namespace'
+import Edit from './Edit'
+import GatheringPlaceGraphics from '../graphics/GatheringPlaceGraphics'
+
+class EditGatheringPlace extends Edit {
+ constructor(overlay) {
+ super(overlay)
+ this._graphics = new GatheringPlaceGraphics()
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountedHook() {
+ this._delegate.polygon.hierarchy = new Cesium.CallbackProperty(() => {
+ if (this._positions.length > 1) {
+ this._graphics.positions = this._positions
+ return this._graphics.hierarchy
+ } else {
+ return null
+ }
+ }, false)
+ this._layer.entities.add(this._delegate)
+ }
+}
+
+export default EditGatheringPlace
diff --git a/src/modules/plot/edit/EditPoint.js b/src/modules/plot/edit/EditPoint.js
new file mode 100644
index 00000000..e09b22a1
--- /dev/null
+++ b/src/modules/plot/edit/EditPoint.js
@@ -0,0 +1,50 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-30 22:04:36
+ */
+
+import { Cesium } from '../../../namespace'
+import Edit from './Edit'
+import { Transform } from '../../transform'
+
+class EditPoint extends Edit {
+ constructor(overlay) {
+ super(overlay)
+ this._position = undefined
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountedHook() {
+ this.editTool.tooltipMess = '右击结束编辑'
+ this._position = this._delegate.position.getValue(Cesium.JulianDate.now())
+ this._delegate.position = new Cesium.CallbackProperty(() => {
+ return this._position
+ }, false)
+ this._layer.entities.add(this._delegate)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _stopedHook() {
+ this._overlay.position = Transform.transformCartesianToWGS84(this._position)
+ this._overlay.show = true
+ this._options.onEditStop && this._options.onEditStop(this._overlay)
+ }
+
+ /**
+ *
+ * @param pickedAnchor
+ * @param position
+ * @private
+ */
+ _onAnchorMoving({ pickedAnchor, position }) {
+ this._position = position
+ }
+}
+
+export default EditPoint
diff --git a/src/modules/plot/edit/EditPolygon.js b/src/modules/plot/edit/EditPolygon.js
new file mode 100644
index 00000000..b7a59bd6
--- /dev/null
+++ b/src/modules/plot/edit/EditPolygon.js
@@ -0,0 +1,175 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-30 23:12:09
+ */
+
+import { Cesium } from '../../../namespace'
+import Edit from './Edit'
+import { PlotEventType } from '../../event'
+import { midCartesian } from '../../math'
+import { Transform } from '../../transform'
+
+class EditPolygon extends Edit {
+ constructor(overlay) {
+ super(overlay)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountedHook() {
+ this._delegate.polygon.hierarchy = new Cesium.CallbackProperty((time) => {
+ if (this._positions.length > 2) {
+ return new Cesium.PolygonHierarchy(
+ this._positions.map((item) => item.clone())
+ )
+ } else {
+ return null
+ }
+ }, false)
+ this._layer.entities.add(this._delegate)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _stopedHook() {
+ this._overlay.positions = Transform.transformCartesianArrayToWGS84Array(
+ this._positions.filter((item, index) => index % 2 === 0)
+ )
+ this._overlay.show = true
+ this._options.onEditStop && this._options.onEditStop(this._overlay)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountAnchor() {
+ this._positions = []
+ let positions = this._overlay.delegate.polygon.hierarchy.getValue(
+ Cesium.JulianDate.now()
+ ).positions
+ positions.push(positions[0])
+ for (let i = 0; i < positions.length - 1; i++) {
+ let mid = midCartesian(positions[i], positions[i + 1])
+ this._positions.push(positions[i])
+ this._positions.push(mid)
+ }
+ this._positions.forEach((item, index) => {
+ this.editTool.fire(PlotEventType.CREATE_ANCHOR, {
+ position: item,
+ index: index,
+ isMid: index % 2 !== 0,
+ })
+ })
+ }
+
+ /**
+ *
+ * @param pickedAnchor
+ * @param position
+ * @returns {boolean}
+ * @private
+ */
+ _onEditAnchorStop({ pickedAnchor, position }) {
+ let properties = pickedAnchor.properties.getValue(Cesium.JulianDate.now())
+ let currentIndex = properties.index
+ if (properties.isMid) {
+ let preMidPosition
+ let nextMidPosition
+ let len = this._positions.length
+ if (currentIndex === len - 1) {
+ preMidPosition = midCartesian(
+ this._positions[currentIndex],
+ this._positions[currentIndex - 1]
+ )
+ nextMidPosition = midCartesian(
+ this._positions[currentIndex],
+ this._positions[0]
+ )
+ } else {
+ preMidPosition = midCartesian(
+ this._positions[currentIndex],
+ this._positions[currentIndex - 1]
+ )
+ nextMidPosition = midCartesian(
+ this._positions[currentIndex],
+ this._positions[currentIndex + 1]
+ )
+ }
+ this._positions.splice(
+ currentIndex,
+ 1,
+ preMidPosition,
+ position,
+ nextMidPosition
+ )
+ this.editTool.fire(PlotEventType.CLEAR_ANCHOR)
+ this._positions.forEach((item, index) => {
+ this.editTool.fire(PlotEventType.CREATE_ANCHOR, {
+ position: item,
+ index: index,
+ isMid: index % 2 !== 0,
+ })
+ })
+ }
+ }
+
+ /**
+ *
+ * @param pickedAnchor
+ * @param position
+ * @private
+ */
+ _onAnchorMoving({ pickedAnchor, position }) {
+ let properties = pickedAnchor.properties.getValue(Cesium.JulianDate.now())
+ let currentIndex = properties.index
+ this._positions[currentIndex] = position
+ let len = this._positions.length
+ if (!properties.isMid) {
+ let preAnchorIndex = -1
+ let preMidAnchorIndex = -1
+ let nextAnchorIndex = -1
+ let nextMidAnchorIndex = -1
+ if (currentIndex === 0) {
+ preAnchorIndex = len - 2
+ preMidAnchorIndex = len - 1
+ nextAnchorIndex = currentIndex + 2
+ nextMidAnchorIndex = currentIndex + 1
+ } else if (currentIndex === len - 2) {
+ preAnchorIndex = currentIndex - 2
+ preMidAnchorIndex = currentIndex - 1
+ nextAnchorIndex = 0
+ nextMidAnchorIndex = len - 1
+ } else {
+ preAnchorIndex = currentIndex - 2
+ preMidAnchorIndex = currentIndex - 1
+ nextAnchorIndex = currentIndex + 2
+ nextMidAnchorIndex = currentIndex + 1
+ }
+ let preMidPosition = midCartesian(
+ this._positions[preAnchorIndex],
+ this._positions[currentIndex]
+ )
+ let nextMidPosition = midCartesian(
+ this._positions[nextAnchorIndex],
+ this._positions[currentIndex]
+ )
+ this._positions[preMidAnchorIndex] = preMidPosition
+ this._positions[nextMidAnchorIndex] = nextMidPosition
+ this.editTool.fire(PlotEventType.UPDATE_ANCHOR, {
+ index: preMidAnchorIndex,
+ position: preMidPosition,
+ })
+ this.editTool.fire(PlotEventType.UPDATE_ANCHOR, {
+ index: nextMidAnchorIndex,
+ position: nextMidPosition,
+ })
+ }
+ }
+}
+
+export default EditPolygon
diff --git a/src/modules/plot/edit/EditPolyline.js b/src/modules/plot/edit/EditPolyline.js
new file mode 100644
index 00000000..6ba24cba
--- /dev/null
+++ b/src/modules/plot/edit/EditPolyline.js
@@ -0,0 +1,162 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-30 22:39:34
+ */
+
+import { Cesium } from '../../../namespace'
+import Edit from './Edit'
+import { PlotEventType } from '../../event'
+import { midCartesian } from '../../math'
+import { Transform } from '../../transform'
+
+class EditPolyline extends Edit {
+ constructor(overlay) {
+ super(overlay)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountedHook() {
+ this._delegate.polyline.positions = new Cesium.CallbackProperty(() => {
+ if (this._positions.length > 1) {
+ return this._positions
+ } else {
+ return null
+ }
+ }, false)
+ this._layer.entities.add(this._delegate)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _stopedHook() {
+ this._overlay.positions = Transform.transformCartesianArrayToWGS84Array(
+ this._positions.filter((item, index) => index % 2 === 0)
+ )
+ this._overlay.show = true
+ this._options.onEditStop && this._options.onEditStop(this._overlay)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountAnchor() {
+ this._positions = []
+ let positions = this._overlay.delegate.polyline.positions.getValue(
+ Cesium.JulianDate.now()
+ )
+ for (let i = 0; i < positions.length - 1; i++) {
+ let mid = midCartesian(positions[i], positions[i + 1])
+ this._positions.push(positions[i])
+ this._positions.push(mid)
+ }
+ this._positions.push(positions[positions.length - 1])
+ this._positions.forEach((item, index) => {
+ this.editTool.fire(PlotEventType.CREATE_ANCHOR, {
+ position: item,
+ index: index,
+ isMid: index % 2 !== 0,
+ })
+ })
+ }
+
+ /**
+ *
+ * @param pickedAnchor
+ * @param position
+ * @returns {boolean}
+ * @private
+ */
+ _onEditAnchorStop({ pickedAnchor, position }) {
+ let properties = pickedAnchor.properties.getValue(Cesium.JulianDate.now())
+ let currentIndex = properties.index
+ if (properties.isMid) {
+ let preMidPosition = midCartesian(
+ this._positions[currentIndex],
+ this._positions[currentIndex - 1]
+ )
+ let nextMidPosition = midCartesian(
+ this._positions[currentIndex],
+ this._positions[currentIndex + 1]
+ )
+ this._positions.splice(
+ currentIndex,
+ 1,
+ preMidPosition,
+ position,
+ nextMidPosition
+ )
+ this.editTool.fire(PlotEventType.CLEAR_ANCHOR)
+ this._positions.forEach((item, index) => {
+ this.editTool.fire(PlotEventType.CREATE_ANCHOR, {
+ position: item,
+ index: index,
+ isMid: index % 2 !== 0,
+ })
+ })
+ }
+ }
+
+ /**
+ *
+ * @param pickedAnchor
+ * @param position
+ * @private
+ */
+ _onAnchorMoving({ pickedAnchor, position }) {
+ let properties = pickedAnchor.properties.getValue(Cesium.JulianDate.now())
+ let currentIndex = properties.index
+ this._positions[currentIndex] = position
+ if (!properties.isMid && this._options.maxAnchorSize > 2) {
+ let preAnchorIndex = -1
+ let preMidAnchorIndex = -1
+ let nextAnchorIndex = -1
+ let nextMidAnchorIndex = -1
+ let len = this._positions.length
+
+ if (currentIndex === 0) {
+ nextAnchorIndex = currentIndex + 2
+ nextMidAnchorIndex = currentIndex + 1
+ } else if (properties.index === len - 1) {
+ preAnchorIndex = currentIndex - 2
+ preMidAnchorIndex = currentIndex - 1
+ } else {
+ preAnchorIndex = currentIndex - 2
+ preMidAnchorIndex = currentIndex - 1
+ nextAnchorIndex = currentIndex + 2
+ nextMidAnchorIndex = currentIndex + 1
+ }
+
+ if (preAnchorIndex > 0) {
+ let preMidPosition = midCartesian(
+ this._positions[preAnchorIndex],
+ this._positions[currentIndex]
+ )
+ this._positions[preMidAnchorIndex] = preMidPosition
+ this.editTool.fire(PlotEventType.UPDATE_ANCHOR, {
+ index: preMidAnchorIndex,
+ position: preMidPosition,
+ })
+ }
+
+ if (nextAnchorIndex > 0) {
+ let nextMidPosition = midCartesian(
+ this._positions[nextAnchorIndex],
+ this._positions[currentIndex]
+ )
+ this._positions[nextMidAnchorIndex] = nextMidPosition
+ this.editTool.fire(PlotEventType.UPDATE_ANCHOR, {
+ index: nextMidAnchorIndex,
+ position: nextMidPosition,
+ })
+ }
+ }
+ }
+}
+
+export default EditPolyline
diff --git a/src/modules/plot/edit/EditRectangle.js b/src/modules/plot/edit/EditRectangle.js
new file mode 100644
index 00000000..8b471d72
--- /dev/null
+++ b/src/modules/plot/edit/EditRectangle.js
@@ -0,0 +1,34 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-30 23:41:34
+ */
+
+import { Cesium } from '../../../namespace'
+import Edit from './Edit'
+
+class EditRectangle extends Edit {
+ constructor(overlay) {
+ super(overlay)
+ this._overlay = overlay
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountedHook() {
+ this._delegate.rectangle.coordinates = new Cesium.CallbackProperty(
+ (time) => {
+ if (this._positions.length > 1) {
+ return Cesium.Rectangle.fromCartesianArray(this._positions)
+ } else {
+ return null
+ }
+ },
+ false
+ )
+ this._layer.entities.add(this._delegate)
+ }
+}
+
+export default EditRectangle
diff --git a/src/modules/plot/edit/EditTailedAttackArrow.js b/src/modules/plot/edit/EditTailedAttackArrow.js
new file mode 100644
index 00000000..8008ae33
--- /dev/null
+++ b/src/modules/plot/edit/EditTailedAttackArrow.js
@@ -0,0 +1,33 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-30 23:46:07
+ */
+
+import { Cesium } from '../../../namespace'
+import Edit from './Edit'
+import TailedAttackArrowGraphics from '../graphics/TailedAttackArrowGraphics'
+
+class EditTailedAttackArrow extends Edit {
+ constructor(overlay) {
+ super(overlay)
+ this._graphics = new TailedAttackArrowGraphics()
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountedHook() {
+ this._delegate.polygon.hierarchy = new Cesium.CallbackProperty(() => {
+ if (this._positions.length > 2) {
+ this._graphics.positions = this._positions
+ return this._graphics.hierarchy
+ } else {
+ return null
+ }
+ }, false)
+ this._layer.entities.add(this._delegate)
+ }
+}
+
+export default EditTailedAttackArrow
diff --git a/src/modules/plot/graphics/AttackArrowGraphics.js b/src/modules/plot/graphics/AttackArrowGraphics.js
new file mode 100644
index 00000000..5d587169
--- /dev/null
+++ b/src/modules/plot/graphics/AttackArrowGraphics.js
@@ -0,0 +1,172 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-30 16:22:50
+ */
+
+import { Cesium } from '../../../namespace'
+import Parse from '../../parse/Parse'
+import { Transform } from '../../transform'
+import { PlotUtil } from '../../utils'
+
+const HALF_PI = Math.PI / 2
+
+class AttackArrowGraphics {
+ constructor(options) {
+ this._positions = options?.positions || []
+ this.headHeightFactor = 0.18
+ this.headWidthFactor = 0.3
+ this.neckHeightFactor = 0.85
+ this.neckWidthFactor = 0.15
+ this.headTailFactor = 0.8
+ }
+
+ set positions(positions) {
+ this._positions = positions
+ }
+
+ get positions() {
+ return this._positions
+ }
+
+ get hierarchy() {
+ return this._createHierarchy()
+ }
+
+ _getArrowHeadPoints(points, tailLeft, tailRight) {
+ let len = PlotUtil.getBaseLength(points)
+ let headHeight = len * this.headHeightFactor
+ let headPnt = points[points.length - 1]
+ len = PlotUtil.distance(headPnt, points[points.length - 2])
+ let tailWidth = PlotUtil.distance(tailLeft, tailRight)
+ if (headHeight > tailWidth * this.headTailFactor) {
+ headHeight = tailWidth * this.headTailFactor
+ }
+ let headWidth = headHeight * this.headWidthFactor
+ let neckWidth = headHeight * this.neckWidthFactor
+ headHeight = headHeight > len ? len : headHeight
+ let neckHeight = headHeight * this.neckHeightFactor
+ let headEndPnt = PlotUtil.getThirdPoint(
+ points[points.length - 2],
+ headPnt,
+ 0,
+ headHeight,
+ true
+ )
+ let neckEndPnt = PlotUtil.getThirdPoint(
+ points[points.length - 2],
+ headPnt,
+ 0,
+ neckHeight,
+ true
+ )
+ let headLeft = PlotUtil.getThirdPoint(
+ headPnt,
+ headEndPnt,
+ HALF_PI,
+ headWidth,
+ false
+ )
+ let headRight = PlotUtil.getThirdPoint(
+ headPnt,
+ headEndPnt,
+ HALF_PI,
+ headWidth,
+ true
+ )
+ let neckLeft = PlotUtil.getThirdPoint(
+ headPnt,
+ neckEndPnt,
+ HALF_PI,
+ neckWidth,
+ false
+ )
+ let neckRight = PlotUtil.getThirdPoint(
+ headPnt,
+ neckEndPnt,
+ HALF_PI,
+ neckWidth,
+ true
+ )
+ return [neckLeft, headLeft, headPnt, headRight, neckRight]
+ }
+
+ _getArrowBodyPoints(points, neckLeft, neckRight, tailWidthFactor) {
+ let allLen = PlotUtil.wholeDistance(points)
+ let len = PlotUtil.getBaseLength(points)
+ let tailWidth = len * tailWidthFactor
+ let neckWidth = PlotUtil.distance(neckLeft, neckRight)
+ let widthDif = (tailWidth - neckWidth) / 2
+ let tempLen = 0
+ let leftBodyPnts = []
+ let rightBodyPnts = []
+ for (let i = 1; i < points.length - 1; i++) {
+ let angle =
+ PlotUtil.getAngleOfThreePoints(
+ points[i - 1],
+ points[i],
+ points[i + 1]
+ ) / 2
+ tempLen += PlotUtil.distance(points[i - 1], points[i])
+ let w = (tailWidth / 2 - (tempLen / allLen) * widthDif) / Math.sin(angle)
+ let left = PlotUtil.getThirdPoint(
+ points[i - 1],
+ points[i],
+ Math.PI - angle,
+ w,
+ true
+ )
+ let right = PlotUtil.getThirdPoint(
+ points[i - 1],
+ points[i],
+ angle,
+ w,
+ false
+ )
+ leftBodyPnts.push(left)
+ rightBodyPnts.push(right)
+ }
+ return leftBodyPnts.concat(rightBodyPnts)
+ }
+
+ _createHierarchy() {
+ let pnts = Parse.parsePolygonCoordToArray(
+ Transform.transformCartesianArrayToWGS84Array(this._positions)
+ )[0]
+ let tailLeft = pnts[0]
+ let tailRight = pnts[1]
+ if (PlotUtil.isClockWise(pnts[0], pnts[1], pnts[2])) {
+ tailLeft = pnts[1]
+ tailRight = pnts[0]
+ }
+ let midTail = PlotUtil.mid(tailLeft, tailRight)
+ let bonePnts = [midTail].concat(pnts.slice(2))
+ // 计算箭头
+ let headPnts = this._getArrowHeadPoints(bonePnts, tailLeft, tailRight)
+ let neckLeft = headPnts[0]
+ let neckRight = headPnts[4]
+ let tailWidthFactor =
+ PlotUtil.distance(tailLeft, tailRight) / PlotUtil.getBaseLength(bonePnts)
+ // 计算箭身
+ let bodyPnts = this._getArrowBodyPoints(
+ bonePnts,
+ neckLeft,
+ neckRight,
+ tailWidthFactor
+ )
+ // 整合
+ let count = bodyPnts.length
+ let leftPnts = [tailLeft].concat(bodyPnts.slice(0, count / 2))
+ leftPnts.push(neckLeft)
+ let rightPnts = [tailRight].concat(bodyPnts.slice(count / 2, count))
+ rightPnts.push(neckRight)
+ leftPnts = PlotUtil.getQBSplinePoints(leftPnts)
+ rightPnts = PlotUtil.getQBSplinePoints(rightPnts)
+ return new Cesium.PolygonHierarchy(
+ Transform.transformWGS84ArrayToCartesianArray(
+ Parse.parsePositions(leftPnts.concat(headPnts, rightPnts.reverse()))
+ )
+ )
+ }
+}
+
+export default AttackArrowGraphics
diff --git a/src/modules/plot/graphics/DoubleArrowGraphics.js b/src/modules/plot/graphics/DoubleArrowGraphics.js
new file mode 100644
index 00000000..3f8428bb
--- /dev/null
+++ b/src/modules/plot/graphics/DoubleArrowGraphics.js
@@ -0,0 +1,243 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-30 16:27:29
+ */
+
+import { Cesium } from '../../../namespace'
+import Parse from '../../parse/Parse'
+import { Transform } from '../../transform'
+import { PlotUtil } from '../../utils'
+
+const HALF_PI = Math.PI / 2
+
+class DoubleArrowGraphics {
+ constructor(options) {
+ this._positions = options?.positions || []
+ this.headHeightFactor = 0.25
+ this.headWidthFactor = 0.3
+ this.neckHeightFactor = 0.85
+ this.neckWidthFactor = 0.15
+ }
+
+ set positions(positions) {
+ this._positions = positions
+ }
+
+ get positions() {
+ return this._positions
+ }
+
+ get hierarchy() {
+ return this._createHierarchy()
+ }
+
+ _getArrowPoints(pnt1, pnt2, pnt3, clockWise) {
+ let midPnt = PlotUtil.mid(pnt1, pnt2)
+ let len = PlotUtil.distance(midPnt, pnt3)
+ let midPnt1 = PlotUtil.getThirdPoint(pnt3, midPnt, 0, len * 0.3, true)
+ let midPnt2 = PlotUtil.getThirdPoint(pnt3, midPnt, 0, len * 0.5, true)
+ midPnt1 = PlotUtil.getThirdPoint(
+ midPnt,
+ midPnt1,
+ HALF_PI,
+ len / 5,
+ clockWise
+ )
+ midPnt2 = PlotUtil.getThirdPoint(
+ midPnt,
+ midPnt2,
+ HALF_PI,
+ len / 4,
+ clockWise
+ )
+ let points = [midPnt, midPnt1, midPnt2, pnt3]
+ // 计算箭头部分
+ let arrowPnts = this._getArrowHeadPoints(points)
+ let neckLeftPoint = arrowPnts[0]
+ let neckRightPoint = arrowPnts[4]
+ // 计算箭身部分
+ let tailWidthFactor =
+ PlotUtil.distance(pnt1, pnt2) / PlotUtil.getBaseLength(points) / 2
+ let bodyPnts = this._getArrowBodyPoints(
+ points,
+ neckLeftPoint,
+ neckRightPoint,
+ tailWidthFactor
+ )
+ let n = bodyPnts.length
+ let lPoints = bodyPnts.slice(0, n / 2)
+ let rPoints = bodyPnts.slice(n / 2, n)
+ lPoints.push(neckLeftPoint)
+ rPoints.push(neckRightPoint)
+ lPoints = lPoints.reverse()
+ lPoints.push(pnt2)
+ rPoints = rPoints.reverse()
+ rPoints.push(pnt1)
+ return lPoints.reverse().concat(arrowPnts, rPoints)
+ }
+
+ _getArrowHeadPoints(points) {
+ let len = PlotUtil.getBaseLength(points)
+ let headHeight = len * this.headHeightFactor
+ let headPnt = points[points.length - 1]
+ let headWidth = headHeight * this.headWidthFactor
+ let neckWidth = headHeight * this.neckWidthFactor
+ let neckHeight = headHeight * this.neckHeightFactor
+ let headEndPnt = PlotUtil.getThirdPoint(
+ points[points.length - 2],
+ headPnt,
+ 0,
+ headHeight,
+ true
+ )
+ let neckEndPnt = PlotUtil.getThirdPoint(
+ points[points.length - 2],
+ headPnt,
+ 0,
+ neckHeight,
+ true
+ )
+ let headLeft = PlotUtil.getThirdPoint(
+ headPnt,
+ headEndPnt,
+ HALF_PI,
+ headWidth,
+ false
+ )
+ let headRight = PlotUtil.getThirdPoint(
+ headPnt,
+ headEndPnt,
+ HALF_PI,
+ headWidth,
+ true
+ )
+ let neckLeft = PlotUtil.getThirdPoint(
+ headPnt,
+ neckEndPnt,
+ HALF_PI,
+ neckWidth,
+ false
+ )
+ let neckRight = PlotUtil.getThirdPoint(
+ headPnt,
+ neckEndPnt,
+ HALF_PI,
+ neckWidth,
+ true
+ )
+ return [neckLeft, headLeft, headPnt, headRight, neckRight]
+ }
+
+ _getArrowBodyPoints(points, neckLeft, neckRight, tailWidthFactor) {
+ let allLen = PlotUtil.wholeDistance(points)
+ let len = PlotUtil.getBaseLength(points)
+ let tailWidth = len * tailWidthFactor
+ let neckWidth = PlotUtil.distance(neckLeft, neckRight)
+ let widthDif = (tailWidth - neckWidth) / 2
+ let tempLen = 0
+ let leftBodyPnts = []
+ let rightBodyPnts = []
+ for (let i = 1; i < points.length - 1; i++) {
+ let angle =
+ PlotUtil.getAngleOfThreePoints(
+ points[i - 1],
+ points[i],
+ points[i + 1]
+ ) / 2
+ tempLen += PlotUtil.distance(points[i - 1], points[i])
+ let w = (tailWidth / 2 - (tempLen / allLen) * widthDif) / Math.sin(angle)
+ let left = PlotUtil.getThirdPoint(
+ points[i - 1],
+ points[i],
+ Math.PI - angle,
+ w,
+ true
+ )
+ let right = PlotUtil.getThirdPoint(
+ points[i - 1],
+ points[i],
+ angle,
+ w,
+ false
+ )
+ leftBodyPnts.push(left)
+ rightBodyPnts.push(right)
+ }
+ return leftBodyPnts.concat(rightBodyPnts)
+ }
+
+ _getTempPoint4(linePnt1, linePnt2, point) {
+ let midPnt = PlotUtil.mid(linePnt1, linePnt2)
+ let len = PlotUtil.distance(midPnt, point)
+ let angle = PlotUtil.getAngleOfThreePoints(linePnt1, midPnt, point)
+ let symPnt, distance1, distance2, mid
+ if (angle < HALF_PI) {
+ distance1 = len * Math.sin(angle)
+ distance2 = len * Math.cos(angle)
+ mid = PlotUtil.getThirdPoint(linePnt1, midPnt, HALF_PI, distance1, false)
+ symPnt = PlotUtil.getThirdPoint(midPnt, mid, HALF_PI, distance2, true)
+ } else if (angle >= HALF_PI && angle < Math.PI) {
+ distance1 = len * Math.sin(Math.PI - angle)
+ distance2 = len * Math.cos(Math.PI - angle)
+ mid = PlotUtil.getThirdPoint(linePnt1, midPnt, HALF_PI, distance1, false)
+ symPnt = PlotUtil.getThirdPoint(midPnt, mid, HALF_PI, distance2, false)
+ } else if (angle >= Math.PI && angle < Math.PI * 1.5) {
+ distance1 = len * Math.sin(angle - Math.PI)
+ distance2 = len * Math.cos(angle - Math.PI)
+ mid = PlotUtil.getThirdPoint(linePnt1, midPnt, HALF_PI, distance1, true)
+ symPnt = PlotUtil.getThirdPoint(midPnt, mid, HALF_PI, distance2, true)
+ } else {
+ distance1 = len * Math.sin(Math.PI * 2 - angle)
+ distance2 = len * Math.cos(Math.PI * 2 - angle)
+ mid = PlotUtil.getThirdPoint(linePnt1, midPnt, HALF_PI, distance1, true)
+ symPnt = PlotUtil.getThirdPoint(midPnt, mid, HALF_PI, distance2, false)
+ }
+ return symPnt
+ }
+
+ _createHierarchy() {
+ let count = this._positions.length
+ let tempPoint4 = undefined
+ let connPoint = undefined
+ let pnts = Parse.parsePolygonCoordToArray(
+ Transform.transformCartesianArrayToWGS84Array(this._positions)
+ )[0]
+ let pnt1 = pnts[0]
+ let pnt2 = pnts[1]
+ let pnt3 = pnts[2]
+ if (count === 3) tempPoint4 = this._getTempPoint4(pnt1, pnt2, pnt3)
+ else tempPoint4 = pnts[3]
+ if (count === 3 || count === 4) connPoint = PlotUtil.mid(pnt1, pnt2)
+ else connPoint = pnts[4]
+ let leftArrowPnts, rightArrowPnts
+ if (PlotUtil.isClockWise(pnt1, pnt2, pnt3)) {
+ leftArrowPnts = this._getArrowPoints(pnt1, connPoint, tempPoint4, false)
+ rightArrowPnts = this._getArrowPoints(connPoint, pnt2, pnt3, true)
+ } else {
+ leftArrowPnts = this._getArrowPoints(pnt2, connPoint, pnt3, false)
+ rightArrowPnts = this._getArrowPoints(connPoint, pnt1, tempPoint4, true)
+ }
+ let m = leftArrowPnts.length
+ let t = (m - 5) / 2
+ let llBodyPnts = leftArrowPnts.slice(0, t)
+ let lArrowPnts = leftArrowPnts.slice(t, t + 5)
+ let lrBodyPnts = leftArrowPnts.slice(t + 5, m)
+ let rlBodyPnts = rightArrowPnts.slice(0, t)
+ let rArrowPnts = rightArrowPnts.slice(t, t + 5)
+ let rrBodyPnts = rightArrowPnts.slice(t + 5, m)
+ rlBodyPnts = PlotUtil.getBezierPoints(rlBodyPnts)
+ let bodyPnts = PlotUtil.getBezierPoints(
+ rrBodyPnts.concat(llBodyPnts.slice(1))
+ )
+ lrBodyPnts = PlotUtil.getBezierPoints(lrBodyPnts)
+ return new Cesium.PolygonHierarchy(
+ Transform.transformWGS84ArrayToCartesianArray(
+ Parse.parsePositions(
+ rlBodyPnts.concat(rArrowPnts, bodyPnts, lArrowPnts, lrBodyPnts)
+ )
+ )
+ )
+ }
+}
+
+export default DoubleArrowGraphics
diff --git a/src/modules/plot/graphics/FineArrowGraphics.js b/src/modules/plot/graphics/FineArrowGraphics.js
new file mode 100644
index 00000000..8763acbd
--- /dev/null
+++ b/src/modules/plot/graphics/FineArrowGraphics.js
@@ -0,0 +1,97 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-30 17:10:33
+ */
+
+import { Cesium } from '../../../namespace'
+import Parse from '../../parse/Parse'
+import { Transform } from '../../transform'
+import { PlotUtil } from '../../utils'
+
+const HALF_PI = Math.PI / 2
+
+class FineArrowGraphics {
+ constructor(options) {
+ this._positions = options?.positions || []
+ this.tailWidthFactor = 0.15
+ this.neckWidthFactor = 0.2
+ this.headWidthFactor = 0.25
+ this.headAngle = Math.PI / 8.5
+ this.neckAngle = Math.PI / 13
+ }
+
+ set positions(positions) {
+ this._positions = positions
+ }
+
+ get positions() {
+ return this._positions
+ }
+
+ get hierarchy() {
+ return this._createHierarchy()
+ }
+
+ _createHierarchy() {
+ let pnts = Parse.parsePolygonCoordToArray(
+ Transform.transformCartesianArrayToWGS84Array(this._positions)
+ )[0]
+ let pnt1 = pnts[0]
+ let pnt2 = pnts[1]
+ let len = PlotUtil.getBaseLength(pnts)
+ let tailWidth = len * this.tailWidthFactor
+ let neckWidth = len * this.neckWidthFactor
+ let headWidth = len * this.headWidthFactor
+ let tailLeft = PlotUtil.getThirdPoint(pnt2, pnt1, HALF_PI, tailWidth, true)
+ let tailRight = PlotUtil.getThirdPoint(
+ pnt2,
+ pnt1,
+ HALF_PI,
+ tailWidth,
+ false
+ )
+ let headLeft = PlotUtil.getThirdPoint(
+ pnt1,
+ pnt2,
+ this.headAngle,
+ headWidth,
+ false
+ )
+ let headRight = PlotUtil.getThirdPoint(
+ pnt1,
+ pnt2,
+ this.headAngle,
+ headWidth,
+ true
+ )
+ let neckLeft = PlotUtil.getThirdPoint(
+ pnt1,
+ pnt2,
+ this.neckAngle,
+ neckWidth,
+ false
+ )
+ let neckRight = PlotUtil.getThirdPoint(
+ pnt1,
+ pnt2,
+ this.neckAngle,
+ neckWidth,
+ true
+ )
+ return new Cesium.PolygonHierarchy(
+ Transform.transformWGS84ArrayToCartesianArray(
+ Parse.parsePositions([
+ tailLeft,
+ neckLeft,
+ headLeft,
+ pnt2,
+ headRight,
+ neckRight,
+ tailRight,
+ ])
+ )
+ )
+ }
+}
+
+export default FineArrowGraphics
diff --git a/src/modules/plot/graphics/GatheringPlaceGraphics.js b/src/modules/plot/graphics/GatheringPlaceGraphics.js
new file mode 100644
index 00000000..0b1f509e
--- /dev/null
+++ b/src/modules/plot/graphics/GatheringPlaceGraphics.js
@@ -0,0 +1,78 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-30 17:17:52
+ */
+
+import { Cesium } from '../../../namespace'
+import Parse from '../../parse/Parse'
+import { Transform } from '../../transform'
+import { PlotUtil } from '../../utils'
+
+const HALF_PI = Math.PI / 2
+
+const FITTING_COUNT = 100
+
+class GatheringPlaceGraphics {
+ constructor(options) {
+ this._positions = options?.positions || []
+ this.t = 0.4
+ }
+
+ set positions(positions) {
+ this._positions = positions
+ }
+
+ get positions() {
+ return this._positions
+ }
+
+ get hierarchy() {
+ return this._createHierarchy()
+ }
+
+ _createHierarchy() {
+ let pnts = Parse.parsePolygonCoordToArray(
+ Transform.transformCartesianArrayToWGS84Array(this._positions)
+ )[0]
+ if (this._positions.length === 2) {
+ let mid = PlotUtil.mid(pnts[0], pnts[1])
+ let d = PlotUtil.distance(pnts[0], mid) / 0.9
+ let pnt = PlotUtil.getThirdPoint(pnts[0], mid, HALF_PI, d, true)
+ pnts = [pnts[0], pnt, pnts[1]]
+ }
+ let mid = PlotUtil.mid(pnts[0], pnts[2])
+ pnts.push(mid, pnts[0], pnts[1])
+ let normals = []
+ for (let i = 0; i < pnts.length - 2; i++) {
+ let pnt1 = pnts[i]
+ let pnt2 = pnts[i + 1]
+ let pnt3 = pnts[i + 2]
+ let normalPoints = PlotUtil.getBisectorNormals(this.t, pnt1, pnt2, pnt3)
+ normals = normals.concat(normalPoints)
+ }
+ let count = normals.length
+ normals = [normals[count - 1]].concat(normals.slice(0, count - 1))
+ let pList = []
+ for (let i = 0; i < pnts.length - 2; i++) {
+ let pnt1 = pnts[i]
+ let pnt2 = pnts[i + 1]
+ pList.push(pnt1)
+ for (let t = 0; t <= FITTING_COUNT; t++) {
+ let pnt = PlotUtil.getCubicValue(
+ t / FITTING_COUNT,
+ pnt1,
+ normals[i * 2],
+ normals[i * 2 + 1],
+ pnt2
+ )
+ pList.push(pnt)
+ }
+ pList.push(pnt2)
+ }
+ return new Cesium.PolygonHierarchy(
+ Transform.transformWGS84ArrayToCartesianArray(Parse.parsePositions(pList))
+ )
+ }
+}
+
+export default GatheringPlaceGraphics
diff --git a/src/modules/plot/graphics/TailedAttackArrowGraphics.js b/src/modules/plot/graphics/TailedAttackArrowGraphics.js
new file mode 100644
index 00000000..3052ff63
--- /dev/null
+++ b/src/modules/plot/graphics/TailedAttackArrowGraphics.js
@@ -0,0 +1,76 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-30 17:26:34
+ */
+
+import { Cesium } from '../../../namespace'
+import AttackArrowGraphics from './AttackArrowGraphics'
+import Parse from '../../parse/Parse'
+import { Transform } from '../../transform'
+import { PlotUtil } from '../../utils'
+
+class TailedAttackArrowGraphics extends AttackArrowGraphics {
+ constructor(options) {
+ super(options)
+ this.headHeightFactor = 0.18
+ this.headWidthFactor = 0.3
+ this.neckHeightFactor = 0.85
+ this.neckWidthFactor = 0.15
+ this.tailWidthFactor = 0.1
+ this.headTailFactor = 0.8
+ this.swallowTailFactor = 1
+ }
+
+ _createHierarchy() {
+ let pnts = Parse.parsePolygonCoordToArray(
+ Transform.transformCartesianArrayToWGS84Array(this._positions)
+ )[0]
+ let tailLeft = pnts[0]
+ let tailRight = pnts[1]
+ if (PlotUtil.isClockWise(pnts[0], pnts[1], pnts[2])) {
+ tailLeft = pnts[1]
+ tailRight = pnts[0]
+ }
+ let midTail = PlotUtil.mid(tailLeft, tailRight)
+ let bonePnts = [midTail].concat(pnts.slice(2))
+ let headPnts = this._getArrowHeadPoints(bonePnts, tailLeft, tailRight)
+ let neckLeft = headPnts[0]
+ let neckRight = headPnts[4]
+ let tailWidth = PlotUtil.distance(tailLeft, tailRight)
+ let allLen = PlotUtil.getBaseLength(bonePnts)
+ let len = allLen * this.tailWidthFactor * this.swallowTailFactor
+ let swallowTailPnt = PlotUtil.getThirdPoint(
+ bonePnts[1],
+ bonePnts[0],
+ 0,
+ len,
+ true
+ )
+ let factor = tailWidth / allLen
+ let bodyPnts = this._getArrowBodyPoints(
+ bonePnts,
+ neckLeft,
+ neckRight,
+ factor
+ )
+ let count = bodyPnts.length
+ let leftPnts = [tailLeft].concat(bodyPnts.slice(0, count / 2))
+ leftPnts.push(neckLeft)
+ let rightPnts = [tailRight].concat(bodyPnts.slice(count / 2, count))
+ rightPnts.push(neckRight)
+ leftPnts = PlotUtil.getQBSplinePoints(leftPnts)
+ rightPnts = PlotUtil.getQBSplinePoints(rightPnts)
+ return new Cesium.PolygonHierarchy(
+ Transform.transformWGS84ArrayToCartesianArray(
+ Parse.parsePositions(
+ leftPnts.concat(headPnts, rightPnts.reverse(), [
+ swallowTailPnt,
+ leftPnts[0],
+ ])
+ )
+ )
+ )
+ }
+}
+
+export default TailedAttackArrowGraphics
diff --git a/src/modules/position/Position.js b/src/modules/position/Position.js
new file mode 100644
index 00000000..e75469f0
--- /dev/null
+++ b/src/modules/position/Position.js
@@ -0,0 +1,221 @@
+/**
+ * @Author: Caven
+ * @Date: 2019-12-27 14:35:02
+ */
+
+import { Cesium } from '../../namespace'
+import { Transform } from '../transform'
+
+class Position {
+ constructor(lng, lat, alt, heading, pitch, roll) {
+ this._lng = +lng || 0
+ this._lat = +lat || 0
+ this._alt = +alt || 0
+ this._heading = +heading || 0
+ this._pitch = +pitch || 0
+ this._roll = +roll || 0
+ }
+
+ set lng(lng) {
+ this._lng = +lng
+ }
+
+ get lng() {
+ return this._lng
+ }
+
+ set lat(lat) {
+ this._lat = +lat
+ }
+
+ get lat() {
+ return this._lat
+ }
+
+ set alt(alt) {
+ this._alt = +alt
+ }
+
+ get alt() {
+ return this._alt
+ }
+
+ set heading(heading) {
+ this._heading = +heading
+ }
+
+ get heading() {
+ return this._heading
+ }
+
+ set pitch(pitch) {
+ this._pitch = +pitch
+ }
+
+ get pitch() {
+ return this._pitch
+ }
+
+ set roll(roll) {
+ this._roll = +roll
+ }
+
+ get roll() {
+ return this._roll
+ }
+
+ /**
+ *
+ * @returns {string}
+ */
+ serialize() {
+ let position = new Position(
+ this._lng,
+ this._lat,
+ this._alt,
+ this._heading,
+ this._pitch,
+ this._roll
+ )
+ return JSON.stringify(position)
+ }
+
+ /**
+ * Calculate the distance between two positions
+ * @param target
+ * @returns {number}
+ */
+ distance(target) {
+ if (!target || !(target instanceof Position)) {
+ return 0
+ }
+ return Cesium.Cartesian3.distance(
+ Transform.transformWGS84ToCartesian(this),
+ Transform.transformWGS84ToCartesian(target)
+ )
+ }
+
+ /**
+ * clone a position
+ * @returns {Position}
+ */
+ clone() {
+ let position = new Position()
+ position.lng = this.lng || 0
+ position.lat = this.lat || 0
+ position.alt = this.alt || 0
+ position.heading = this.heading || 0
+ position.pitch = this.pitch || 0
+ position.roll = this.roll || 0
+ return position
+ }
+
+ /**
+ * clone a position
+ * @deprecated
+ * @returns {Position}
+ */
+ copy() {
+ return this.clone()
+ }
+
+ /**
+ *
+ * @returns {*[]}
+ */
+ toArray() {
+ return [this.lng, this.lat, this.alt, this.heading, this.pitch, this.roll]
+ }
+
+ /**
+ *
+ * @returns {string}
+ */
+ toString() {
+ return `${this.lng},${this.lat},${this.alt},${this.heading},${this.pitch},${this.roll}`
+ }
+
+ /**
+ *
+ * @returns {{lng, heading, alt, roll, pitch, lat}}
+ */
+ toObject() {
+ return {
+ lng: this.lng,
+ lat: this.lat,
+ alt: this.alt,
+ heading: this.heading,
+ pitch: this.pitch,
+ roll: this.roll,
+ }
+ }
+
+ /**
+ *
+ * @param arr
+ * @returns {Position}
+ */
+ static fromArray(arr) {
+ let position = new Position()
+ if (Array.isArray(arr)) {
+ position.lng = arr[0] || 0
+ position.lat = arr[1] || 0
+ position.alt = arr[2] || 0
+ position.heading = arr[3] || 0
+ position.pitch = arr[4] || 0
+ position.roll = arr[5] || 0
+ }
+ return position
+ }
+
+ /**
+ *
+ * @param str
+ * @returns {Position}
+ */
+ static fromString(str) {
+ let position = new Position()
+ if (str && typeof str === 'string') {
+ let arr = str.split(',')
+ position = this.fromArray(arr)
+ }
+ return position
+ }
+
+ /**
+ *
+ * @param obj
+ * @returns {Position}
+ */
+ static fromObject(obj) {
+ return new Position(
+ obj.lng,
+ obj.lat,
+ obj.alt,
+ obj.heading,
+ obj.pitch,
+ obj.roll
+ )
+ }
+
+ /**
+ * Deserialize
+ * @param valStr
+ * @returns {Position}
+ */
+ static deserialize(valStr) {
+ let position = new Position()
+ let obj = JSON.parse(valStr)
+ if (obj) {
+ position.lng = obj.lng || 0
+ position.lat = obj.lat || 0
+ position.alt = obj.alt || 0
+ position.heading = obj.heading || 0
+ position.pitch = obj.pitch || 0
+ position.roll = obj.roll || 0
+ }
+ return position
+ }
+}
+
+export default Position
diff --git a/src/modules/roaming/KeyboardRoaming.js b/src/modules/roaming/KeyboardRoaming.js
new file mode 100644
index 00000000..40e05cfc
--- /dev/null
+++ b/src/modules/roaming/KeyboardRoaming.js
@@ -0,0 +1,189 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-06-08 20:41:51
+ */
+
+import { Cesium } from '../../namespace'
+
+class KeyboardRoaming {
+ constructor(viewer) {
+ this._viewer = viewer
+ this._enable = false
+ this._moveRate = 100.0
+ this._rotateRate = 0.01
+ this._flags = {
+ moveForward: false,
+ moveBackward: false,
+ moveUp: false,
+ moveDown: false,
+ moveLeft: false,
+ moveRight: false,
+ turnLeft: false,
+ turnRight: false,
+ }
+ }
+
+ set enable(enable) {
+ if (this._enable === enable) {
+ return this
+ }
+ if (this._viewer.scene.mode !== Cesium.SceneMode.SCENE3D) {
+ return this
+ }
+ this._enable = enable
+ this._enable ? this._bindEvent() : this._unbindEvent()
+ return this
+ }
+
+ get enable() {
+ return this._enable
+ }
+
+ set moveRate(moveRate) {
+ this._moveRate = moveRate
+ return this
+ }
+
+ get moveRate() {
+ return this._moveRate
+ }
+
+ set rotateRate(rotateRate) {
+ this._rotateRate = rotateRate
+ return this
+ }
+
+ get rotateRate() {
+ return this._rotateRate
+ }
+
+ /**
+ *
+ * @private
+ */
+ _bindEvent() {
+ let canvas = this._viewer.scene.canvas
+ canvas.setAttribute('tabindex', '0')
+ canvas.addEventListener('click', this._onClick.bind(this), false)
+ this._viewer.clock.onTick.addEventListener(this._onTick, this)
+ document.addEventListener('keydown', this._onKeydown.bind(this), false)
+ document.addEventListener('keyup', this._onKeyup.bind(this), false)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _unbindEvent() {
+ Object.keys(this._flags).forEach((key) => {
+ this._flags[key] = false
+ })
+ let canvas = this._viewer.scene.canvas
+ canvas.removeAttribute('tabindex')
+ canvas.removeEventListener('click', this._onClick, false)
+ this._viewer.clock.onTick.removeEventListener(this._onTick, this)
+ document.removeEventListener('keydown', this._onKeydown, false)
+ document.removeEventListener('keyup', this._onKeyup, false)
+ }
+
+ /**
+ *
+ * @param e
+ * @returns {undefined}
+ * @private
+ */
+ _getFlagForKeyCode(e) {
+ let flag = undefined
+ switch (e.keyCode) {
+ case 'W'.charCodeAt(0):
+ case 38:
+ if (e.shiftKey) {
+ flag = 'moveUp'
+ } else {
+ flag = 'moveForward'
+ }
+ break
+ case 'S'.charCodeAt(0):
+ case 40:
+ if (e.shiftKey) {
+ flag = 'moveDown'
+ } else {
+ flag = 'moveBackward'
+ }
+ break
+ case 'A'.charCodeAt(0):
+ case 37:
+ flag = 'moveLeft'
+ break
+ case 'D'.charCodeAt(0):
+ case 39:
+ flag = 'moveRight'
+ break
+ case 'Q'.charCodeAt(0):
+ flag = 'turnLeft'
+ break
+ case 'E'.charCodeAt(0):
+ flag = 'turnRight'
+ break
+ default:
+ break
+ }
+ return flag
+ }
+
+ /**
+ *
+ * @private
+ */
+ _onClick() {
+ let canvas = this._viewer.scene.canvas
+ canvas.focus()
+ }
+
+ /**
+ *
+ * @param e
+ * @private
+ */
+ _onKeydown(e) {
+ let flag = this._getFlagForKeyCode(e)
+ if (flag) {
+ this._flags[flag] = true
+ }
+ }
+
+ /**
+ *
+ * @param e
+ * @private
+ */
+ _onKeyup(e) {
+ Object.keys(this._flags).forEach((key) => {
+ this._flags[key] = false
+ })
+ }
+
+ /**
+ *
+ * @private
+ */
+ _onTick() {
+ let camera = this._viewer.scene.camera
+ let cameraHeight =
+ this._viewer.scene.globe.ellipsoid.cartesianToCartographic(
+ camera.position
+ ).height
+ let moveRate = cameraHeight / this._moveRate
+ let axis = Cesium.Cartesian3.clone(camera.position, new Cesium.Cartesian3())
+ this._flags.moveForward && camera.moveForward(moveRate)
+ this._flags.moveBackward && camera.moveBackward(moveRate)
+ this._flags.moveUp && camera.moveUp(moveRate)
+ this._flags.moveDown && camera.moveDown(moveRate)
+ this._flags.moveLeft && camera.moveLeft(moveRate)
+ this._flags.moveRight && camera.moveRight(moveRate)
+ this._flags.turnLeft && camera.rotate(axis, -this._rotateRate)
+ this._flags.turnRight && camera.rotate(axis, this._rotateRate)
+ }
+}
+
+export default KeyboardRoaming
diff --git a/src/modules/roaming/RoamingController.js b/src/modules/roaming/RoamingController.js
new file mode 100644
index 00000000..03893b9b
--- /dev/null
+++ b/src/modules/roaming/RoamingController.js
@@ -0,0 +1,144 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-06-08 20:41:51
+ */
+
+import { Cesium } from '../../namespace'
+import { SceneEventType, PathEventType } from '../event'
+
+class RoamingController {
+ constructor(viewer) {
+ this._viewer = viewer
+ this._viewOption = {}
+ this._cache = {}
+ this._activedPath = undefined
+ }
+
+ /**
+ *
+ * @returns {boolean}
+ * @private
+ */
+ _onPostRender() {
+ if (!this._activedPath) {
+ return false
+ }
+ this._activedPath.pathEvent &&
+ this._activedPath.pathEvent.fire(PathEventType.POST_RENDER, {
+ viewer: this._viewer,
+ viewOption: this._viewOption,
+ })
+ }
+
+ /**
+ *
+ * @param path
+ * @returns {RoamingController}
+ */
+ addPath(path) {
+ if (path && !this._cache.hasOwnProperty(path.pathId)) {
+ path.pathEvent.fire(PathEventType.ADD)
+ this._cache[path.pathId] = path
+ }
+ return this
+ }
+
+ /**
+ *
+ * @param paths
+ * @returns {RoamingController}
+ */
+ addPaths(paths) {
+ if (Array.isArray(paths)) {
+ paths.forEach((item) => {
+ this.addPath(item)
+ })
+ }
+ return this
+ }
+
+ /**
+ *
+ * @param path
+ * @returns {RoamingController}
+ */
+ removePath(path) {
+ if (path && this._cache.hasOwnProperty(path.pathId)) {
+ delete this._cache[path.pathId]
+ path.pathEvent.fire(PathEventType.REMOVE)
+ }
+ return this
+ }
+
+ /**
+ *
+ * @param id
+ * @returns {*|undefined}
+ */
+ getPath(id) {
+ let filters = this.getPaths().filter((item) => item.id === id)
+ return filters && filters.length ? filters[0] : undefined
+ }
+
+ /**
+ *
+ * @returns {*[]}
+ */
+ getPaths() {
+ let result = []
+ Object.keys(this._cache).forEach((key) => {
+ result.push(this._cache[key])
+ })
+ return result
+ }
+
+ /**
+ *
+ * @param path
+ * @param viewOption
+ * @returns {RoamingController}
+ */
+ activate(path, viewOption = {}) {
+ if (
+ !path ||
+ path?.pathId === this._activedPath?.pathId ||
+ !this._cache.hasOwnProperty(path?.pathId)
+ ) {
+ return this
+ }
+ this._viewOption = viewOption
+ this._activedPath && this.deactivate()
+ this._activedPath = path
+ this._activedPath.pathEvent &&
+ this._activedPath.pathEvent.fire(PathEventType.RESET_TIME_LINE)
+ this._viewer.on(SceneEventType.POST_RENDER, this._onPostRender, this)
+ return this
+ }
+
+ /**
+ *
+ * @returns {RoamingController}
+ */
+ deactivate() {
+ this._activedPath && (this._activedPath.actived = false)
+ this._activedPath = undefined
+ this._viewer.off(SceneEventType.POST_RENDER, this._onPostRender, this)
+ this._viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)
+ return this
+ }
+
+ /**
+ *
+ * @returns {RoamingController}
+ */
+ clear() {
+ this._cache = {}
+ this._activedPath && (this._activedPath.actived = false)
+ this._activedPath = undefined
+ this._viewer.off(SceneEventType.POST_RENDER, this._onPostRender, this)
+ this._viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)
+ return this
+ }
+}
+
+export default RoamingController
diff --git a/src/modules/roaming/RoamingPath.js b/src/modules/roaming/RoamingPath.js
new file mode 100644
index 00000000..80a75e32
--- /dev/null
+++ b/src/modules/roaming/RoamingPath.js
@@ -0,0 +1,178 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-06-08 20:41:51
+ */
+
+import { Cesium } from '../../namespace'
+import State from '../state/State'
+import Parse from '../parse/Parse'
+import { PathEventType, PathEvent } from '../event'
+import { Util } from '../utils'
+import { Transform } from '../transform'
+import { heading, distance } from '../math'
+
+class RoamingPath {
+ constructor(positions, duration, pathMode) {
+ this._id = Util.uuid()
+ this._bid = undefined
+ this._positions = Parse.parsePositions(positions)
+ this._duration = duration || 20
+ this._pathMode = pathMode || 'speed'
+ this._timeLine = []
+ this._sampledPosition = undefined
+ this._actived = false
+ this._endTime = Cesium.JulianDate.now()
+ this._pathEvent = new PathEvent()
+ this._pathEvent.on(PathEventType.ADD, this._onAdd, this)
+ this._pathEvent.on(PathEventType.REMOVE, this._onRemove, this)
+ this._pathEvent.on(PathEventType.POST_RENDER, this._onPostRender, this)
+ this._pathEvent.on(PathEventType.RESET_TIME_LINE, this._resetTimeLine, this)
+ this._state = State.INITIALIZED
+ }
+
+ get pathId() {
+ return this._id
+ }
+
+ set id(id) {
+ this._bid = id
+ }
+
+ get id() {
+ return this._bid
+ }
+
+ set positions(postions) {
+ this._positions = Parse.parsePositions(postions)
+ }
+
+ get positions() {
+ return this._positions
+ }
+
+ set duration(duration) {
+ this._duration = duration
+ }
+
+ get duration() {
+ return this._duration
+ }
+
+ set pathMode(pathMode) {
+ this._pathMode = pathMode
+ }
+
+ get pathMode() {
+ return this._pathMode
+ }
+
+ get pathEvent() {
+ return this._pathEvent
+ }
+
+ set actived(actived) {
+ this._actived = actived
+ }
+
+ get actived() {
+ return this._actived
+ }
+
+ get state() {
+ return this._state
+ }
+
+ _onAdd() {
+ this._state = State.ADDED
+ }
+
+ _onRemove() {
+ this._state = State.REMOVED
+ }
+
+ /**
+ *
+ * @param viewer
+ * @param viewOption
+ * @private
+ */
+ _onPostRender({ viewer, viewOption }) {
+ if (!this.actived) {
+ return false
+ }
+ let now = Cesium.JulianDate.now()
+ if (
+ Cesium.JulianDate.lessThan(now, this._endTime) &&
+ this._sampledPosition
+ ) {
+ let p = this._sampledPosition.getValue(now)
+ let next_p = this._sampledPosition.getValue(
+ Cesium.JulianDate.addSeconds(now, 0.001, new Cesium.JulianDate())
+ )
+ if (p && next_p) {
+ viewer.camera.lookAt(
+ p,
+ new Cesium.HeadingPitchRange(
+ heading(p, next_p),
+ Cesium.Math.toRadians(viewOption?.pitch || -20),
+ viewOption?.range || 2000
+ )
+ )
+ }
+ } else {
+ viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)
+ this._actived = false
+ }
+ }
+
+ /**
+ *
+ * @private
+ */
+ _resetTimeLine() {
+ if (!this._positions || !this._positions.length) {
+ return false
+ }
+ let now = Cesium.JulianDate.now()
+ let interval = 0
+ let timeLine = []
+ if (this._pathMode === 'speed') {
+ let v = distance(this._positions) / this._duration
+ timeLine = this._positions.map((item, index, arr) => {
+ if (index !== 0) {
+ interval += distance([arr[index - 1], item]) / v
+ }
+ return Cesium.JulianDate.addSeconds(
+ now,
+ interval,
+ new Cesium.JulianDate()
+ )
+ })
+ } else {
+ let len = this._positions.length
+ let interval = (this._duration - (this._duration % len)) / len
+ timeLine = this._positions.map((item, index) => {
+ return Cesium.JulianDate.addSeconds(
+ now,
+ index * interval,
+ new Cesium.JulianDate()
+ )
+ })
+ }
+ this._sampledPosition = new Cesium.SampledPositionProperty()
+ this._sampledPosition.addSamples(
+ timeLine,
+ Transform.transformWGS84ArrayToCartesianArray(this._positions)
+ )
+ this._sampledPosition.forwardExtrapolationType =
+ Cesium.ExtrapolationType.HOLD
+ this._sampledPosition.setInterpolationOptions({
+ interpolationDegree: 2,
+ interpolationAlgorithm: Cesium.HermitePolynomialApproximation,
+ })
+ this._endTime = timeLine[timeLine.length - 1]
+ this._actived = true
+ }
+}
+
+export default RoamingPath
diff --git a/src/modules/roaming/index.js b/src/modules/roaming/index.js
new file mode 100644
index 00000000..f98f1931
--- /dev/null
+++ b/src/modules/roaming/index.js
@@ -0,0 +1,11 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-06-08 20:41:51
+ */
+
+/**
+ * roaming
+ */
+export { default as KeyboardRoaming } from './KeyboardRoaming'
+export { default as RoamingController } from './RoamingController'
+export { default as RoamingPath } from './RoamingPath'
diff --git a/src/modules/state/State.js b/src/modules/state/State.js
new file mode 100644
index 00000000..1c4de4fb
--- /dev/null
+++ b/src/modules/state/State.js
@@ -0,0 +1,19 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-05-11 16:39:23
+ */
+
+const State = {
+ INITIALIZED: 'initialized',
+ ADDED: 'added',
+ REMOVED: 'removed',
+ CLEARED: 'cleared',
+ INSTALLED: 'installed',
+ ENABLED: 'enabled',
+ DISABLED: 'disabled',
+ PLAY: 'play',
+ PAUSE: 'pause',
+ RESTORE: 'restore',
+}
+
+export default State
diff --git a/src/modules/terrain/TerrainFactory.js b/src/modules/terrain/TerrainFactory.js
new file mode 100644
index 00000000..244768c7
--- /dev/null
+++ b/src/modules/terrain/TerrainFactory.js
@@ -0,0 +1,86 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-21 15:54:56
+ */
+
+import { Cesium } from '../../namespace'
+import TerrainType from './TerrainType'
+
+class TerrainFactory {
+ /**
+ *
+ * @param options
+ * @returns {module:@cesium/engine.EllipsoidTerrainProvider}
+ */
+ static createEllipsoidTerrain(options) {
+ return new Cesium.EllipsoidTerrainProvider(options)
+ }
+
+ /**
+ * Create url terrain
+ * @param options
+ * @returns {module:cesium.CesiumTerrainProvider}
+ */
+ static createUrlTerrain(options) {
+ return new Cesium.CesiumTerrainProvider(options)
+ }
+
+ /**
+ * Create google terrain
+ * @param options
+ * @returns {module:cesium.GoogleEarthEnterpriseTerrainProvider}
+ */
+ static createGoogleTerrain(options) {
+ return new Cesium.GoogleEarthEnterpriseTerrainProvider(options)
+ }
+
+ /**
+ * Create arcgis terrain
+ * @param options
+ * @returns {module:cesium.ArcGISTiledElevationTerrainProvider}
+ */
+ static createArcgisTerrain(options) {
+ return new Cesium.ArcGISTiledElevationTerrainProvider(options)
+ }
+
+ /**
+ * Create vr terrain
+ * @param options
+ * @returns {module:cesium.VRTheWorldTerrainProvider}
+ */
+ static createVRTerrain(options) {
+ return new Cesium.VRTheWorldTerrainProvider(options)
+ }
+
+ /**
+ * Create Terrain
+ * @param type
+ * @param options
+ * @returns {any}
+ */
+ static createTerrain(type, options) {
+ let terrain = undefined
+ switch (type) {
+ case TerrainType.NONE:
+ terrain = this.createEllipsoidTerrain(options)
+ break
+ case TerrainType.XYZ:
+ terrain = this.createUrlTerrain(options)
+ break
+ case TerrainType.GOOGLE:
+ terrain = this.createGoogleTerrain(options)
+ break
+ case TerrainType.ARCGIS:
+ terrain = this.createArcgisTerrain(options)
+ break
+ case TerrainType.VR:
+ terrain = this.createVRTerrain(options)
+ break
+ default:
+ break
+ }
+ return terrain
+ }
+}
+
+export default TerrainFactory
diff --git a/src/modules/terrain/TerrainType.js b/src/modules/terrain/TerrainType.js
new file mode 100644
index 00000000..63eaaeeb
--- /dev/null
+++ b/src/modules/terrain/TerrainType.js
@@ -0,0 +1,14 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-05-10 08:15:36
+ */
+
+let TerrainType = {
+ NONE: 'none',
+ XYZ: 'xyz',
+ ARCGIS: 'arcgis',
+ GOOGLE: 'google',
+ VR: 'vr',
+}
+
+export default TerrainType
diff --git a/src/modules/terrain/index.js b/src/modules/terrain/index.js
new file mode 100644
index 00000000..2b6e7b6f
--- /dev/null
+++ b/src/modules/terrain/index.js
@@ -0,0 +1,7 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-03-14 00:30:53
+ */
+
+export { default as TerrainType } from './TerrainType'
+export { default as TerrainFactory } from './TerrainFactory'
diff --git a/src/modules/themes/compass.scss b/src/modules/themes/compass.scss
new file mode 100644
index 00000000..ccb03f5a
--- /dev/null
+++ b/src/modules/themes/compass.scss
@@ -0,0 +1,56 @@
+.dc-compass {
+ position: absolute;
+ top: 20px;
+ right: 20px;
+ cursor: pointer;
+ pointer-events: auto;
+ user-select: none;
+ width: 55px;
+ height: 55px;
+
+ .out-ring {
+ position: absolute;
+ top: 0;
+ left: 0;
+ height: 55px;
+ width: 55px;
+ background-repeat: no-repeat;
+ background-size: contain;
+ fill: #3f4854;
+ border-radius: 50%;
+ svg {
+ height: 55px;
+ width: 55px;
+ }
+ }
+
+ .gyro {
+ position: relative;
+ top: 50%;
+ transform: translateY(-50%);
+ height: 25px;
+ width: 25px;
+ border-radius: 50%;
+ display: block;
+ margin: 0 auto;
+ padding: 4px;
+ box-sizing: border-box;
+ background: #ffffff;
+ }
+
+ .rotation_marker {
+ position: absolute;
+ top: 2px;
+ left: 2px;
+ height: 51px;
+ width: 51px;
+ border-radius: 50%;
+ background-repeat: no-repeat;
+ background-size: contain;
+ }
+}
+
+.dc-compass .gyro-active,
+.dc-compass .gyro-bg:hover + .gyro {
+ fill: #68adfe;
+}
diff --git a/src/modules/themes/contextmenu.scss b/src/modules/themes/contextmenu.scss
new file mode 100644
index 00000000..e425ac33
--- /dev/null
+++ b/src/modules/themes/contextmenu.scss
@@ -0,0 +1,40 @@
+.dc-context-menu {
+ position: absolute;
+ left: 0;
+ top: 0;
+ min-width: 120px;
+ min-height: 10px;
+ background: rgba(43,44,47,.8);
+ border: 1px solid #2b2c2f;
+ border-radius: 4px;
+ visibility: hidden;
+ z-index: -1;
+ cursor: pointer;
+ .menu-list {
+ width: 100%;
+ color: #fff;
+ .menu-item {
+ font-size: 14px;
+ list-style: none;
+ width: 100%;
+ &:nth-child(n+2)::before{
+ content: "";
+ display: block;
+ height: 1px;
+ width: 100%;
+ background: -webkit-linear-gradient(270deg,transparent,hsla(0,0%,100%,.2),transparent);
+ background: linear-gradient(270deg,transparent,hsla(0,0%,100%,.2),transparent);
+ }
+ a{
+ color: #fff;
+ display:block;
+ padding: 6px 10px;
+ clear: both;
+ text-decoration: none;
+ &:hover{
+ background-color: #444d59;
+ }
+ }
+ }
+ }
+}
diff --git a/src/modules/themes/distancelegend.scss b/src/modules/themes/distancelegend.scss
new file mode 100644
index 00000000..485fc0f2
--- /dev/null
+++ b/src/modules/themes/distancelegend.scss
@@ -0,0 +1,23 @@
+.dc-distance-legend{
+ position: absolute;
+ left: 120px;
+ bottom: 2px;
+ width: 125px;
+ height: 25px;
+ user-select: none;
+ .label{
+ font-size: 14px;
+ color: rgb(255, 255, 255);
+ text-align: center;
+ width: 100%;
+ font-weight: lighter;
+ }
+ .scale-bar{
+ position: absolute;
+ height: 10px;
+ top: 10px;
+ border-left: 1px solid rgb(255, 255, 255);
+ border-right: 1px solid rgb(255, 255, 255);
+ border-bottom: 1px solid rgb(255, 255, 255);
+ }
+}
diff --git a/src/modules/themes/hawkeyemap.scss b/src/modules/themes/hawkeyemap.scss
new file mode 100644
index 00000000..20797c89
--- /dev/null
+++ b/src/modules/themes/hawkeyemap.scss
@@ -0,0 +1,12 @@
+.dc-hawkeye-map {
+ position: absolute;
+ left: 25px;
+ bottom: 30px;
+ user-select: none;
+ border-radius: 50%;
+ width: 150px;
+ height: 150px;
+ overflow: hidden;
+ border: 2px solid orange;
+ box-shadow: 2px 2px 3px #2b2b2b;
+}
diff --git a/src/modules/themes/index.js b/src/modules/themes/index.js
new file mode 100644
index 00000000..6ad0d6f7
--- /dev/null
+++ b/src/modules/themes/index.js
@@ -0,0 +1,17 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-21 10:48:50
+ */
+
+import './index.scss'
+import './popup.scss'
+import './tooltip.scss'
+import './contextmenu.scss'
+import './mapswitch.scss'
+import './slider.scss'
+import './hawkeyemap.scss'
+import './compass.scss'
+import './locationbar.scss'
+import './distancelegend.scss'
+import './zoom-controller.scss'
+import './loading-mask.scss'
diff --git a/src/modules/themes/index.scss b/src/modules/themes/index.scss
new file mode 100644
index 00000000..25b2e797
--- /dev/null
+++ b/src/modules/themes/index.scss
@@ -0,0 +1,57 @@
+@charset "UTF-8";
+
+* {
+ padding: 0;
+ margin: 0;
+}
+
+.dc-container {
+ overflow: hidden;
+ display: block;
+}
+
+.dc-viewer {
+ font-family: sans-serif;
+ font-size: 16px;
+ overflow: hidden;
+ display: block;
+ position: relative;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+}
+
+.dc-viewer-widget-container {
+ width: 100%;
+ height: 100%;
+}
+
+.dc-widget {
+ font-family: sans-serif;
+ font-size: 16px;
+ overflow: hidden;
+ display: block;
+ position: relative;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+}
+
+.dc-widget,
+.dc-widget canvas {
+ width: 100%;
+ height: 100%;
+ touch-action: none;
+}
+
+.div-icon {
+ user-select: none;
+ background-color: #fff;
+ padding: 2px 2px;
+ border-radius: 4px;
+ &:hover {
+ cursor: pointer;
+ }
+}
diff --git a/src/modules/themes/loading-mask.scss b/src/modules/themes/loading-mask.scss
new file mode 100644
index 00000000..3ff37c08
--- /dev/null
+++ b/src/modules/themes/loading-mask.scss
@@ -0,0 +1,63 @@
+.dc-loading-mask {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background: rgba(0,0,0,0.6);
+
+ .loading {
+ width: 150px;
+ height: 15px;
+ margin: 0 auto;
+ }
+
+ .loading span {
+ display: inline-block;
+ width: 15px;
+ height: 100%;
+ margin-right: 5px;
+ border-radius: 50%;
+ background: #b8e9ff;
+ -webkit-animation: load 1.04s ease infinite;
+ }
+
+ .loading span:last-child {
+ margin-right: 0;
+ }
+
+ @-webkit-keyframes load {
+ 0% {
+ opacity: 1;
+ -webkit-transform: scale(1.3);
+ }
+
+ 100% {
+ opacity: 0.2;
+ -webkit-transform: scale(0.3);
+ }
+ }
+
+ .loading span:nth-child(1) {
+ -webkit-animation-delay: 0.13s;
+ }
+
+ .loading span:nth-child(2) {
+ -webkit-animation-delay: 0.26s;
+ }
+
+ .loading span:nth-child(3) {
+ -webkit-animation-delay: 0.39s;
+ }
+
+ .loading span:nth-child(4) {
+ -webkit-animation-delay: 0.52s;
+ }
+
+ .loading span:nth-child(5) {
+ -webkit-animation-delay: 0.65s;
+ }
+}
diff --git a/src/modules/themes/locationbar.scss b/src/modules/themes/locationbar.scss
new file mode 100644
index 00000000..aabd4bb3
--- /dev/null
+++ b/src/modules/themes/locationbar.scss
@@ -0,0 +1,43 @@
+.dc-location-bar {
+ position: absolute;
+ left: 270px;
+ bottom: 2px;
+ font-size: 14px;
+ color: rgb(255, 255, 255);
+ background: rgba(0, 0, 0, 0.6);
+ padding: 2px 5px;
+ border-radius: 2px;
+ user-select: none;
+ display: flex;
+ .mouse-bar, .camera-bar ,.ms-bar,.fps-bar{
+ display: flex;
+ span {
+ margin: 0 8px;
+ }
+
+ }
+
+ .mouse-bar {
+ span {
+ min-width: 140px;
+ }
+ span:nth-of-type(3) {
+ min-width: 120px;
+ }
+ }
+
+ .camera-bar {
+ span{
+ min-width: 90px;
+ }
+ span:nth-of-type(2) {
+ min-width: 145px;
+ }
+ }
+
+ .ms-bar,.fps-bar {
+ span {
+ min-width: 70px;
+ }
+ }
+}
diff --git a/src/modules/themes/mapswitch.scss b/src/modules/themes/mapswitch.scss
new file mode 100644
index 00000000..8a07ddd5
--- /dev/null
+++ b/src/modules/themes/mapswitch.scss
@@ -0,0 +1,45 @@
+.dc-map-switch {
+ position: absolute;
+ right: 10px;
+ bottom: 5px;
+ width: 80px;
+ height: 60px;
+ background: #fff;
+ transition: width 2s;
+ -moz-transition: width 2s; /* Firefox 4 */
+ -webkit-transition: width 2s; /* Safari and Chrome */
+ -o-transition: width 2s; /* Opera */
+ padding: 5px 5px;
+ box-sizing: content-box;
+ box-shadow: 2px 2px 3px #888888;
+ white-space: nowrap;
+ cursor: pointer;
+ overflow: hidden;
+ visibility: hidden;
+ .map-item {
+ position: relative;
+ display: inline-block;
+ margin-right: 5px;
+ width: 80px;
+ height: 60px;
+ overflow: hidden;
+ span {
+ color: #fff;
+ font-size: 14px;
+ position: absolute;
+ right: 2px;
+ bottom: 2px;
+ user-select: none;
+ padding: 1px 3px;
+ pointer-events: none;
+ }
+ &.active {
+ span {
+ background-color: rgba(8, 101, 186, 0.8);
+ }
+ }
+ &:last-child {
+ margin-right: 0 !important;
+ }
+ }
+}
diff --git a/src/modules/themes/popup.scss b/src/modules/themes/popup.scss
new file mode 100644
index 00000000..b6f66c4f
--- /dev/null
+++ b/src/modules/themes/popup.scss
@@ -0,0 +1,13 @@
+.dc-popup {
+ position: absolute;
+ left: 0;
+ top: 0;
+ min-width: 120px;
+ min-height: 10px;
+ padding: 10px 10px;
+ background: rgba(255, 255, 255, 1);
+ border-radius: 4px;
+ visibility: hidden;
+ z-index: -1;
+ cursor: pointer;
+}
diff --git a/src/modules/themes/slider.scss b/src/modules/themes/slider.scss
new file mode 100644
index 00000000..8b4036f8
--- /dev/null
+++ b/src/modules/themes/slider.scss
@@ -0,0 +1,25 @@
+.dc-slider {
+ position: absolute;
+ left: 50%;
+ top: 0px;
+ background-color: #d3d3d3;
+ width: 5px;
+ height: 100%;
+ z-index: 9999;
+ .splitter {
+ position: absolute;
+ left: -21px;
+ top: calc(50% - 21px);
+ width: 42px;
+ height: 42px;
+ background: #fff;
+ border-radius: 50%;
+ text-align: center;
+ line-height: 58px;
+ padding: 2px;
+ border: 1px solid lightgrey;
+ &:hover {
+ cursor: ew-resize;
+ }
+ }
+}
diff --git a/src/modules/themes/tooltip.scss b/src/modules/themes/tooltip.scss
new file mode 100644
index 00000000..ee705a67
--- /dev/null
+++ b/src/modules/themes/tooltip.scss
@@ -0,0 +1,26 @@
+.dc-tool-tip {
+ position: absolute;
+ left: 0;
+ top: 0;
+ min-width: 100px;
+ height: 30px;
+ line-height: 30px;
+ color: #fff;
+ padding: 0 10px;
+ background: rgba(0, 0, 0, 0.6);
+ border-radius: 4px;
+ visibility: hidden;
+ pointer-events: none;
+ z-index: -1;
+ &:before {
+ content: '';
+ display: block;
+ position: absolute;
+ pointer-events: none;
+ left: -10px;
+ top: 5px;
+ border-top: 10px solid transparent;
+ border-bottom: 10px solid transparent;
+ border-right: 10px solid rgba(0, 0, 0, 0.6);
+ }
+}
diff --git a/src/modules/themes/zoom-controller.scss b/src/modules/themes/zoom-controller.scss
new file mode 100644
index 00000000..f3c3289c
--- /dev/null
+++ b/src/modules/themes/zoom-controller.scss
@@ -0,0 +1,23 @@
+.dc-zoom-controller{
+ position: absolute;
+ top: 90px;
+ right: 36.5px;
+ pointer-events: auto;
+ user-select: none;
+ background: #3f4854;
+ border-radius: 100px;
+ border: solid 1px rgba(255,255,255,0.2);
+ text-align: center;
+ box-sizing: border-box;
+ line-height: 1.2rem;
+ width: 24px;
+ height: 60px;
+ .zoom-in ,.refresh,.zoom-out{
+ cursor: pointer;
+ svg{
+ width: 10px;
+ height: 10px;
+ fill: #fff;
+ }
+ }
+}
diff --git a/src/modules/thirdpart/index.js b/src/modules/thirdpart/index.js
new file mode 100644
index 00000000..5f1ee35d
--- /dev/null
+++ b/src/modules/thirdpart/index.js
@@ -0,0 +1,41 @@
+/**
+ * @Author: Caven
+ * @Date: 2019-12-30 09:35:51
+ */
+
+import { Cesium } from '../../namespace'
+
+const thirdPart = {
+ Cartesian2: Cesium.Cartesian2,
+ Cartesian3: Cesium.Cartesian3,
+ Matrix3: Cesium.Matrix3,
+ Matrix4: Cesium.Matrix4,
+ Material: Cesium.Material,
+ SceneMode: Cesium.SceneMode,
+ SkyBox: Cesium.SkyBox,
+ Color: Cesium.Color,
+ Rect: Cesium.Rectangle,
+ ShadowMode: Cesium.ShadowMode,
+ JulianDate: Cesium.JulianDate,
+ HeightReference: Cesium.HeightReference,
+ HorizontalOrigin: Cesium.HorizontalOrigin,
+ VerticalOrigin: Cesium.VerticalOrigin,
+ CallbackProperty: Cesium.CallbackProperty,
+ ClassificationType: Cesium.ClassificationType,
+ TilesetStyle: Cesium.Cesium3DTileStyle,
+ GeographicTilingScheme: Cesium.GeographicTilingScheme,
+ WebMercatorTilingScheme: Cesium.WebMercatorTilingScheme,
+ ColorMaterialProperty: Cesium.ColorMaterialProperty,
+ ImageMaterialProperty: Cesium.ImageMaterialProperty,
+ PolylineDashMaterialProperty: Cesium.PolylineDashMaterialProperty,
+ PolylineGlowMaterialProperty: Cesium.PolylineGlowMaterialProperty,
+ PolylineOutlineMaterialProperty: Cesium.PolylineOutlineMaterialProperty,
+ PolylineArrowMaterialProperty: Cesium.PolylineArrowMaterialProperty,
+ writeTextToCanvas: Cesium.writeTextToCanvas,
+ CustomShaderMode: Cesium.CustomShaderMode,
+ CustomShaderTranslucencyMode: Cesium.CustomShaderTranslucencyMode,
+ CustomShader: Cesium.CustomShader,
+ UniformType: Cesium.UniformType,
+}
+
+export default thirdPart
diff --git a/src/modules/tools/DrawTool.js b/src/modules/tools/DrawTool.js
new file mode 100644
index 00000000..4bf7f9fe
--- /dev/null
+++ b/src/modules/tools/DrawTool.js
@@ -0,0 +1,210 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-07-14 20:28:14
+ */
+
+import { Cesium } from '../../namespace'
+import { MouseEventType, PlotEventType, PlotEvent } from '../event'
+import IMG_CIRCLE_RED from '../images/circle_red.png'
+import IMG_CIRCLE_YELLOW from '../images/circle_yellow.png'
+
+const DEF_OPTS = {
+ icon_center: IMG_CIRCLE_YELLOW,
+ icon_anchor: IMG_CIRCLE_RED,
+ icon_size: [12, 12],
+ clampToModel: false,
+ maxAnchorSize: 999,
+}
+
+class DrawTool {
+ constructor() {
+ this._viewer = undefined
+ this._anchorLayer = new Cesium.CustomDataSource('draw-anchor-layer')
+ this._floatingAnchor = undefined
+ this._options = {}
+ this._plotEvent = new PlotEvent()
+ this._tooltipMess = undefined
+ }
+
+ set tooltipMess(tooltipMess) {
+ this._tooltipMess = tooltipMess
+ }
+
+ /**
+ *
+ * @param e
+ * @returns {boolean}
+ * @private
+ */
+ _onClick(e) {
+ let position =
+ this._options.clampToModel && e.position ? e.position : e.surfacePosition
+ if (!position) {
+ return false
+ }
+ if (!this._floatingAnchor) {
+ this._floatingAnchor = this._onCreateAnchor({ position })
+ }
+ this._plotEvent.fire(PlotEventType.DRAW_ANCHOR, position)
+ }
+
+ /**
+ *
+ * @param e
+ * @private
+ */
+ _onMouseMove(e) {
+ this._viewer.tooltip.showAt(e.windowPosition, this._tooltipMess)
+ let position =
+ this._options.clampToModel && e.position ? e.position : e.surfacePosition
+ if (!position) {
+ return false
+ }
+ this._floatingAnchor && this._floatingAnchor.position.setValue(position)
+ this._plotEvent.fire(PlotEventType.ANCHOR_MOVING, position)
+ }
+
+ /**
+ *
+ * @param e
+ * @private
+ */
+ _onRightClick(e) {
+ this._plotEvent.fire(
+ PlotEventType.DRAW_STOP,
+ this._options.clampToModel && e.position ? e.position : e.surfacePosition
+ )
+ }
+
+ /**
+ *
+ * @param position
+ * @param isCenter
+ * @returns {*}
+ * @private
+ */
+ _onCreateAnchor({ position, isCenter = false }) {
+ return this._anchorLayer.entities.add({
+ position: position,
+ billboard: {
+ image: isCenter ? this._options.icon_center : this._options.icon_anchor,
+ width: this._options.icon_size[0],
+ height: this._options.icon_size[1],
+ eyeOffset: new Cesium.Cartesian3(0, 0, -100),
+ heightReference:
+ this._viewer.scene.mode === Cesium.SceneMode.SCENE3D &&
+ !this._options.clampToModel
+ ? Cesium.HeightReference.CLAMP_TO_GROUND
+ : Cesium.HeightReference.NONE,
+ },
+ })
+ }
+
+ /**
+ *
+ * @private
+ */
+ _onClearAnchor() {
+ this._anchorLayer.entities.removeAll()
+ }
+
+ /**
+ *
+ * @private
+ */
+ _bindEvent() {
+ this._viewer.on(MouseEventType.CLICK, this._onClick, this)
+ this._viewer.on(MouseEventType.MOUSE_MOVE, this._onMouseMove, this)
+ this._viewer.on(MouseEventType.RIGHT_CLICK, this._onRightClick, this)
+ this._plotEvent.on(PlotEventType.CREATE_ANCHOR, this._onCreateAnchor, this)
+ this._plotEvent.on(PlotEventType.CLEAR_ANCHOR, this._onClearAnchor, this)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _unbindEvent() {
+ this._viewer.off(MouseEventType.CLICK, this._onClick, this)
+ this._viewer.off(MouseEventType.MOUSE_MOVE, this._onMouseMove, this)
+ this._viewer.off(MouseEventType.RIGHT_CLICK, this._onRightClick, this)
+ this._plotEvent.off(PlotEventType.CREATE_ANCHOR, this._onCreateAnchor, this)
+ this._plotEvent.off(PlotEventType.CLEAR_ANCHOR, this._onClearAnchor, this)
+ }
+
+ /**
+ *
+ * @param type
+ * @param callback
+ * @param context
+ * @returns {DrawTool}
+ */
+ on(type, callback, context) {
+ this._plotEvent.on(type, callback, context || this)
+ return this
+ }
+
+ /**
+ *
+ * @param type
+ * @param callback
+ * @param context
+ * @returns {DrawTool}
+ */
+ off(type, callback, context) {
+ this._plotEvent.off(type, callback, context || this)
+ return this
+ }
+
+ /**
+ *
+ * @param type
+ * @param params
+ * @returns {DrawTool}
+ */
+ fire(type, params = {}) {
+ this._plotEvent.fire(type, params)
+ return this
+ }
+
+ /**
+ *
+ * @param options
+ * @returns {DrawTool}
+ */
+ activate(options = {}) {
+ this._viewer.tooltip.enable = true
+ this._options = { ...DEF_OPTS, ...options }
+ this._unbindEvent()
+ this._bindEvent()
+ this.fire(PlotEventType.DRAW_START, this._options)
+ return this
+ }
+
+ /**
+ *
+ * @returns {DrawTool}
+ */
+ deactivate() {
+ this._unbindEvent()
+ this._viewer.tooltip.enable = false
+ this._anchorLayer.entities.removeAll()
+ this._floatingAnchor = undefined
+ return this
+ }
+
+ /**
+ *
+ * @param viewer
+ */
+ install(viewer) {
+ this._viewer = viewer
+ this._viewer.dataSources.add(this._anchorLayer)
+ Object.defineProperty(this._viewer, 'drawTool', {
+ value: this,
+ writable: false,
+ })
+ }
+}
+
+export default DrawTool
diff --git a/src/modules/tools/EditTool.js b/src/modules/tools/EditTool.js
new file mode 100644
index 00000000..3e959963
--- /dev/null
+++ b/src/modules/tools/EditTool.js
@@ -0,0 +1,275 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-07-14 20:28:10
+ */
+
+import { Cesium } from '../../namespace'
+import { MouseEventType, PlotEventType, PlotEvent } from '../event'
+import IMG_CIRCLE_RED from '../images/circle_red.png'
+import IMG_CIRCLE_BLUE from '../images/circle_blue.png'
+import IMG_CIRCLE_YELLOW from '../images/circle_yellow.png'
+
+const DEF_OPTS = {
+ icon_center: IMG_CIRCLE_YELLOW,
+ icon_anchor: IMG_CIRCLE_RED,
+ icon_midAnchor: IMG_CIRCLE_BLUE,
+ icon_size: [12, 12],
+ clampToModel: true,
+}
+
+class EditTool {
+ constructor() {
+ this._viewer = undefined
+ this._anchorLayer = new Cesium.CustomDataSource('edit-anchor-layer')
+ this._options = {}
+ this._plotEvent = new PlotEvent()
+ this._tooltipMess = undefined
+ this._pickedAnchor = undefined
+ this._isMoving = false
+ this._anchors = []
+ }
+
+ set tooltipMess(tooltipMess) {
+ this._tooltipMess = tooltipMess
+ }
+
+ /**
+ *
+ * @param e
+ * @returns {boolean}
+ * @private
+ */
+ _onClick(e) {
+ if (this._isMoving) {
+ let position =
+ this._options.clampToModel && e.position
+ ? e.position
+ : e.surfacePosition
+ if (!position) {
+ return false
+ }
+ if (
+ this._pickedAnchor &&
+ this._pickedAnchor.position &&
+ this._pickedAnchor.properties
+ ) {
+ this._pickedAnchor.position.setValue(position)
+ this._plotEvent.fire(PlotEventType.EDIT_ANCHOR_STOP, {
+ pickedAnchor: this._pickedAnchor,
+ position,
+ })
+ }
+ this._isMoving = false
+ } else {
+ if (!e.target || !e.target.id) {
+ return false
+ }
+ this._pickedAnchor = e.target.id
+ this._isMoving = true
+ }
+ }
+
+ /**
+ *
+ * @param e
+ * @private
+ */
+ _onMouseMove(e) {
+ this._viewer.tooltip.showAt(e.windowPosition, this._tooltipMess)
+ if (!this._isMoving && this._anchors.length !== 0) {
+ return false
+ }
+ let position =
+ this._options.clampToModel && e.position ? e.position : e.surfacePosition
+ if (!position) {
+ return false
+ }
+ if (
+ this._pickedAnchor &&
+ this._pickedAnchor.position &&
+ this._pickedAnchor.properties
+ ) {
+ this._pickedAnchor.position.setValue(position)
+ this._plotEvent.fire(PlotEventType.ANCHOR_MOVING, {
+ pickedAnchor: this._pickedAnchor,
+ position,
+ })
+ } else if (this._anchors.length === 0) {
+ this._plotEvent.fire(PlotEventType.ANCHOR_MOVING, {
+ position,
+ })
+ }
+ }
+
+ /**
+ *
+ * @param e
+ * @private
+ */
+ _onRightClick(e) {
+ let position =
+ this._options.clampToModel && e.position ? e.position : e.surfacePosition
+ this._plotEvent.fire(PlotEventType.EDIT_STOP, {
+ pickedAnchor: this._pickedAnchor,
+ position,
+ })
+ }
+
+ /**
+ *
+ * @param position
+ * @param index
+ * @param isCenter
+ * @param isMid
+ * @private
+ */
+ _onCreateAnchor({ position, index, isCenter = false, isMid = false }) {
+ let image = isMid
+ ? this._options.icon_midAnchor
+ : isCenter
+ ? this._options.icon_center
+ : this._options.icon_anchor
+ let anchor = this._anchorLayer.entities.add({
+ position: position,
+ billboard: {
+ image: image,
+ width: this._options.icon_size[0],
+ height: this._options.icon_size[1],
+ eyeOffset: new Cesium.Cartesian3(0, 0, -100),
+ heightReference:
+ this._viewer.scene.mode === Cesium.SceneMode.SCENE3D &&
+ !this._options.clampToModel
+ ? Cesium.HeightReference.CLAMP_TO_GROUND
+ : Cesium.HeightReference.NONE,
+ },
+ properties: {
+ isMid: isMid,
+ index: index,
+ },
+ })
+ this._anchors.push(anchor)
+ }
+
+ /**
+ *
+ * @param index
+ * @param position
+ * @private
+ */
+ _onUpdateAnchor({ index, position }) {
+ this._anchors[index] && this._anchors[index].position.setValue(position)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _onClearAnchor() {
+ this._anchorLayer.entities.removeAll()
+ this._anchors = []
+ }
+
+ /**
+ *
+ * @private
+ */
+ _bindEvent() {
+ this._viewer.on(MouseEventType.CLICK, this._onClick, this)
+ this._viewer.on(MouseEventType.MOUSE_MOVE, this._onMouseMove, this)
+ this._viewer.on(MouseEventType.RIGHT_CLICK, this._onRightClick, this)
+ this._plotEvent.on(PlotEventType.CREATE_ANCHOR, this._onCreateAnchor, this)
+ this._plotEvent.on(PlotEventType.UPDATE_ANCHOR, this._onUpdateAnchor, this)
+ this._plotEvent.on(PlotEventType.CLEAR_ANCHOR, this._onClearAnchor, this)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _unbindEvent() {
+ this._viewer.off(MouseEventType.CLICK, this._onClick, this)
+ this._viewer.off(MouseEventType.MOUSE_MOVE, this._onMouseMove, this)
+ this._viewer.off(MouseEventType.RIGHT_CLICK, this._onRightClick, this)
+ this._plotEvent.off(PlotEventType.CREATE_ANCHOR, this._onCreateAnchor, this)
+ this._plotEvent.off(PlotEventType.UPDATE_ANCHOR, this._onUpdateAnchor, this)
+ this._plotEvent.off(PlotEventType.CLEAR_ANCHOR, this._onClearAnchor, this)
+ }
+
+ /**
+ *
+ * @param type
+ * @param callback
+ * @param context
+ * @returns {EditTool}
+ */
+ on(type, callback, context) {
+ this._plotEvent.on(type, callback, context || this)
+ return this
+ }
+
+ /**
+ *
+ * @param type
+ * @param callback
+ * @param context
+ * @returns {EditTool}
+ */
+ off(type, callback, context) {
+ this._plotEvent.off(type, callback, context || this)
+ return this
+ }
+
+ /**
+ *
+ * @param type
+ * @param parmas
+ * @returns {EditTool}
+ */
+ fire(type, parmas) {
+ this._plotEvent.fire(type, parmas)
+ return this
+ }
+
+ /**
+ *
+ * @param options
+ * @returns {EditTool}
+ */
+ activate(options = {}) {
+ this._anchorLayer.entities.removeAll()
+ this._anchors = []
+ this._viewer.tooltip.enable = true
+ this._options = { ...DEF_OPTS, ...options }
+ this._unbindEvent()
+ this._bindEvent()
+ this.fire(PlotEventType.EDIT_START, this._options)
+ return this
+ }
+
+ /**
+ *
+ * @returns {EditTool}
+ */
+ deactivate() {
+ this._unbindEvent()
+ this._viewer.tooltip.enable = false
+ this._anchorLayer.entities.removeAll()
+ this._anchors = []
+ return this
+ }
+
+ /**
+ *
+ * @param viewer
+ */
+ install(viewer) {
+ this._viewer = viewer
+ this._viewer.dataSources.add(this._anchorLayer)
+ Object.defineProperty(this._viewer, 'editTool', {
+ value: this,
+ writable: false,
+ })
+ }
+}
+
+export default EditTool
diff --git a/src/modules/tools/index.js b/src/modules/tools/index.js
new file mode 100644
index 00000000..d7b88a9a
--- /dev/null
+++ b/src/modules/tools/index.js
@@ -0,0 +1,14 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-07-14 20:25:41
+ */
+
+import DrawTool from './DrawTool'
+import EditTool from './EditTool'
+
+export default function createTools() {
+ return {
+ drawTool: new DrawTool(),
+ editTool: new EditTool(),
+ }
+}
diff --git a/src/modules/transform/CoordTransform.js b/src/modules/transform/CoordTransform.js
new file mode 100644
index 00000000..87ed1a13
--- /dev/null
+++ b/src/modules/transform/CoordTransform.js
@@ -0,0 +1,184 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-01-31 20:40:25
+ */
+
+const BD_FACTOR = (3.14159265358979324 * 3000.0) / 180.0
+const PI = 3.1415926535897932384626
+const RADIUS = 6378245.0
+const EE = 0.00669342162296594323
+
+class CoordTransform {
+ /**
+ * BD-09 To GCJ-02
+ * @param lng
+ * @param lat
+ * @returns {number[]}
+ */
+ static BD09ToGCJ02(lng, lat) {
+ let x = +lng - 0.0065
+ let y = +lat - 0.006
+ let z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * BD_FACTOR)
+ let theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * BD_FACTOR)
+ let gg_lng = z * Math.cos(theta)
+ let gg_lat = z * Math.sin(theta)
+ return [gg_lng, gg_lat]
+ }
+
+ /**
+ * GCJ-02 To BD-09
+ * @param lng
+ * @param lat
+ * @returns {number[]}
+ * @constructor
+ */
+ static GCJ02ToBD09(lng, lat) {
+ lat = +lat
+ lng = +lng
+ let z =
+ Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * BD_FACTOR)
+ let theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * BD_FACTOR)
+ let bd_lng = z * Math.cos(theta) + 0.0065
+ let bd_lat = z * Math.sin(theta) + 0.006
+ return [bd_lng, bd_lat]
+ }
+
+ /**
+ * WGS-84 To GCJ-02
+ * @param lng
+ * @param lat
+ * @returns {number[]}
+ */
+ static WGS84ToGCJ02(lng, lat) {
+ lat = +lat
+ lng = +lng
+ if (this.out_of_china(lng, lat)) {
+ return [lng, lat]
+ } else {
+ let d = this.delta(lng, lat)
+ return [lng + d[0], lat + d[1]]
+ }
+ }
+
+ /**
+ * GCJ-02 To WGS-84
+ * @param lng
+ * @param lat
+ * @returns {number[]}
+ * @constructor
+ */
+ static GCJ02ToWGS84(lng, lat) {
+ lat = +lat
+ lng = +lng
+ if (this.out_of_china(lng, lat)) {
+ return [lng, lat]
+ } else {
+ let out = [lng, lat]
+
+ let gcj02_point = this.WGS84ToGCJ02(lng, lat)
+ let dlng = gcj02_point[0] - lng
+ let dlat = gcj02_point[1] - lat
+ do {
+ gcj02_point = this.WGS84ToGCJ02((out[0] -= dlng), (out[1] -= dlat))
+ dlng = gcj02_point[0] - lng
+ dlat = gcj02_point[1] - lat
+ } while (Math.abs(dlng) > 1e-7 || Math.abs(dlat) > 1e-7)
+
+ return out
+ }
+ }
+
+ /**
+ *
+ * @param lng
+ * @param lat
+ * @returns {number[]}
+ */
+ static delta(lng, lat) {
+ let dLng = this.transformLng(lng - 105, lat - 35)
+ let dLat = this.transformLat(lng - 105, lat - 35)
+ const radLat = (lat / 180) * PI
+ let magic = Math.sin(radLat)
+ magic = 1 - EE * magic * magic
+ const sqrtMagic = Math.sqrt(magic)
+ dLng = (dLng * 180) / ((RADIUS / sqrtMagic) * Math.cos(radLat) * PI)
+ dLat = (dLat * 180) / (((RADIUS * (1 - EE)) / (magic * sqrtMagic)) * PI)
+ return [dLng, dLat]
+ }
+
+ /**
+ *
+ * @param lng
+ * @param lat
+ * @returns {number}
+ */
+ static transformLng(lng, lat) {
+ lat = +lat
+ lng = +lng
+ let ret =
+ 300.0 +
+ lng +
+ 2.0 * lat +
+ 0.1 * lng * lng +
+ 0.1 * lng * lat +
+ 0.1 * Math.sqrt(Math.abs(lng))
+ ret +=
+ ((20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) *
+ 2.0) /
+ 3.0
+ ret +=
+ ((20.0 * Math.sin(lng * PI) + 40.0 * Math.sin((lng / 3.0) * PI)) * 2.0) /
+ 3.0
+ ret +=
+ ((150.0 * Math.sin((lng / 12.0) * PI) +
+ 300.0 * Math.sin((lng / 30.0) * PI)) *
+ 2.0) /
+ 3.0
+ return ret
+ }
+
+ /**
+ *
+ * @param lng
+ * @param lat
+ * @returns {number}
+ */
+ static transformLat(lng, lat) {
+ lat = +lat
+ lng = +lng
+ let ret =
+ -100.0 +
+ 2.0 * lng +
+ 3.0 * lat +
+ 0.2 * lat * lat +
+ 0.1 * lng * lat +
+ 0.2 * Math.sqrt(Math.abs(lng))
+ ret +=
+ ((20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) *
+ 2.0) /
+ 3.0
+ ret +=
+ ((20.0 * Math.sin(lat * PI) + 40.0 * Math.sin((lat / 3.0) * PI)) * 2.0) /
+ 3.0
+ ret +=
+ ((160.0 * Math.sin((lat / 12.0) * PI) +
+ 320 * Math.sin((lat * PI) / 30.0)) *
+ 2.0) /
+ 3.0
+ return ret
+ }
+
+ /**
+ *
+ * @param lng
+ * @param lat
+ * @returns {boolean}
+ */
+ static out_of_china(lng, lat) {
+ lat = +lat
+ lng = +lng
+ return !(lng > 73.66 && lng < 135.05 && lat > 3.86 && lat < 53.55)
+ }
+}
+
+export default CoordTransform
diff --git a/src/modules/transform/Transform.js b/src/modules/transform/Transform.js
new file mode 100644
index 00000000..51c48ca8
--- /dev/null
+++ b/src/modules/transform/Transform.js
@@ -0,0 +1,144 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-07 09:00:32
+ */
+
+import { Cesium } from '../../namespace'
+import Position from '../position/Position'
+
+const WMP = new Cesium.WebMercatorProjection()
+
+class Transform {
+ /**
+ * Transforms Cartesian To WGS84
+ * @param cartesian
+ * @returns {Position}
+ */
+ static transformCartesianToWGS84(cartesian) {
+ if (cartesian) {
+ let cartographic =
+ Cesium.Ellipsoid.WGS84.cartesianToCartographic(cartesian)
+ return new Position(
+ Cesium.Math.toDegrees(cartographic?.longitude || 0),
+ Cesium.Math.toDegrees(cartographic?.latitude || 0),
+ cartographic.height || 0
+ )
+ }
+ return new Position(0, 0)
+ }
+
+ /**
+ * Transforms WGS84 To Cartesian
+ * @param position
+ * @returns {Cartesian3}
+ */
+ static transformWGS84ToCartesian(position) {
+ return position
+ ? Cesium.Cartesian3.fromDegrees(
+ position.lng,
+ position.lat,
+ position.alt,
+ Cesium.Ellipsoid.WGS84
+ )
+ : Cesium.Cartesian3.ZERO
+ }
+
+ /**
+ * Transforms WGS84 To Cartographic
+ * @param position
+ * @returns {Cartographic}
+ */
+ static transformWGS84ToCartographic(position) {
+ return position
+ ? Cesium.Cartographic.fromDegrees(
+ position.lng,
+ position.lat,
+ position.alt
+ )
+ : Cesium.Cartographic.ZERO
+ }
+
+ /**
+ * Transforms Cartesian Array To WGS84 Array
+ * @param cartesianArr
+ * @returns {*|*[]}
+ */
+ static transformCartesianArrayToWGS84Array(cartesianArr) {
+ return cartesianArr
+ ? cartesianArr.map((item) => this.transformCartesianToWGS84(item))
+ : []
+ }
+
+ /**
+ * Transforms WGS84 Array To Cartesian Array
+ * @param WGS84Arr
+ * @returns {*|*[]}
+ */
+ static transformWGS84ArrayToCartesianArray(WGS84Arr) {
+ return WGS84Arr
+ ? WGS84Arr.map((item) => this.transformWGS84ToCartesian(item))
+ : []
+ }
+
+ /**
+ * Transforms WGS84 To Mercator
+ * @param position
+ * @returns {Position}
+ */
+ static transformWGS84ToMercator(position) {
+ let mp = WMP.project(
+ Cesium.Cartographic.fromDegrees(position.lng, position.lat, position.alt)
+ )
+ return new Position(mp.x, mp.y, mp.z)
+ }
+
+ /**
+ * Transforms Mercator To WGS84
+ * @param position
+ * @returns {Position}
+ */
+ static transformMercatorToWGS84(position) {
+ let mp = WMP.unproject(
+ new Cesium.Cartesian3(position.lng, position.lat, position.alt)
+ )
+ return new Position(
+ Cesium.Math.toDegrees(mp.longitude),
+ Cesium.Math.toDegrees(mp.latitude),
+ mp.height
+ )
+ }
+
+ /**
+ * Transforms Window To WGS84
+ * @param position
+ * @param viewer
+ * @returns {Position}
+ */
+ static transformWindowToWGS84(position, viewer) {
+ let scene = viewer.scene
+ let cartesian
+ if (scene.mode === Cesium.SceneMode.SCENE3D) {
+ let ray = scene.camera.getPickRay(position)
+ cartesian = scene.globe.pick(ray, scene)
+ } else {
+ cartesian = scene.camera.pickEllipsoid(position, Cesium.Ellipsoid.WGS84)
+ }
+ return this.transformCartesianToWGS84(cartesian)
+ }
+
+ /**
+ * Transforms WGS84 To Window
+ * @param position
+ * @param viewer
+ * @returns {Cartesian2}
+ */
+ static transformWGS84ToWindow(position, viewer) {
+ let scene = viewer.scene
+ return Cesium.SceneTransforms.wgs84ToWindowCoordinates(
+ scene,
+ this.transformWGS84ToCartesian(position)
+ )
+ }
+}
+
+export default Transform
diff --git a/src/modules/transform/index.js b/src/modules/transform/index.js
new file mode 100644
index 00000000..a4eb81c7
--- /dev/null
+++ b/src/modules/transform/index.js
@@ -0,0 +1,7 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-01-31 21:38:15
+ */
+
+export { default as Transform } from './Transform'
+export { default as CoordTransform } from './CoordTransform'
diff --git a/src/modules/utils/DomUtil.js b/src/modules/utils/DomUtil.js
new file mode 100644
index 00000000..c88bf74b
--- /dev/null
+++ b/src/modules/utils/DomUtil.js
@@ -0,0 +1,249 @@
+/**
+ * @Author: Caven
+ * @Date: 2019-12-31 17:50:13
+ */
+
+import Util from './Util'
+
+/**
+ * Dom Utils
+ * some code reference leaflet
+ * https://github.com/Leaflet/Leaflet/tree/master/src/core
+ */
+class DomUtil {
+ /**
+ * Returns an element given its DOM id, or returns the element itself
+ * if it was passed directly.
+ * @param id
+ * @returns {HTMLElement|*}
+ */
+ static get(id) {
+ return typeof id === 'string' ? document.getElementById(id) : id
+ }
+
+ /**
+ * Returns the value for a certain style attribute on an element,
+ * including computed values or values set through CSS.
+ * @param el
+ * @param style
+ * @returns {null|*}
+ */
+ static getStyle(el, style) {
+ let value = el.style[style] || (el.currentStyle && el.currentStyle[style])
+
+ if ((!value || value === 'auto') && document.defaultView) {
+ let css = document.defaultView.getComputedStyle(el, null)
+ value = css ? css[style] : null
+ }
+ return value === 'auto' ? null : value
+ }
+
+ /**
+ * Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element.
+ * @param tagName
+ * @param className
+ * @param container
+ * @returns {HTMLElement}
+ */
+ static create(tagName, className, container = null) {
+ let el = document.createElement(tagName)
+ el.className = className || ''
+ if (container) {
+ container.appendChild(el)
+ }
+ return el
+ }
+
+ /**
+ * Removes `el` from its parent element
+ * @param {*} el
+ */
+ static remove(el) {
+ let parent = el.parentNode
+ if (parent) {
+ parent.removeChild(el)
+ }
+ }
+
+ /**
+ * Removes all of `el`'s children elements from `el`
+ * @param {*} el
+ */
+ static empty(el) {
+ while (el.firstChild) {
+ el.removeChild(el.firstChild)
+ }
+ }
+
+ /**
+ * Returns `true` if the element's class attribute contains `name`.
+ * @param {*} el
+ * @param {*} name
+ */
+ static hasClass(el, name) {
+ if (el.classList !== undefined) {
+ return el.classList.contains(name)
+ }
+ let className = this.getClass(el)
+ return (
+ className.length > 0 &&
+ new RegExp('(^|\\s)' + name + '(\\s|$)').test(className)
+ )
+ }
+
+ /**
+ * @function Adds `name` to the element's class attribute.
+ * @param {*} el
+ * @param {*} name
+ */
+ static addClass(el, name) {
+ if (el.classList !== undefined) {
+ let classes = Util.splitWords(name)
+ for (let i = 0, len = classes.length; i < len; i++) {
+ el.classList.add(classes[i])
+ }
+ } else if (!this.hasClass(el, name)) {
+ let className = this.getClass(el)
+ this.setClass(el, (className ? className + ' ' : '') + name)
+ }
+ }
+
+ /**
+ * @function Removes `name` from the element's class attribute.
+ * @param {*} el
+ * @param {*} name
+ */
+ static removeClass(el, name) {
+ if (el.classList !== undefined) {
+ el.classList.remove(name)
+ } else {
+ this.setClass(
+ el,
+ Util.trim(
+ (' ' + this.getClass(el) + ' ').replace(' ' + name + ' ', ' ')
+ )
+ )
+ }
+ }
+
+ /**
+ * Sets the element's class.
+ * @param {*} el
+ * @param {*} name
+ */
+ static setClass(el, name) {
+ if (el.className.baseVal === undefined) {
+ el.className = name
+ } else {
+ // in case of SVG element
+ el.className.baseVal = name
+ }
+ }
+
+ /**
+ * @function Returns the element's class.
+ * @param {*} el
+ */
+ static getClass(el) {
+ // Check if the element is an SVGElementInstance and use the correspondingElement instead
+ // (Required for linked SVG elements in IE11.)
+ if (el.correspondingElement) {
+ el = el.correspondingElement
+ }
+ return el.className.baseVal === undefined
+ ? el.className
+ : el.className.baseVal
+ }
+
+ /**
+ * Creates svg
+ * @param width
+ * @param height
+ * @param path
+ * @param container
+ * @returns {SVGElement}
+ */
+ static createSvg(width, height, path, container) {
+ let svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg:svg')
+ svg.setAttribute('class', 'svg-path')
+ svg.setAttribute('width', width)
+ svg.setAttribute('height', height)
+ svg.setAttribute('viewBox', `0 0 ${width} ${height}`)
+ let pathEl = document.createElementNS('http://www.w3.org/2000/svg', 'path')
+ pathEl.setAttribute('d', path)
+ svg.appendChild(pathEl)
+ if (container) {
+ container.appendChild(svg)
+ }
+ return svg
+ }
+
+ /**
+ * Parses string to Dom
+ * @param domStr
+ * @param withWrapper
+ * @param className
+ * @returns {HTMLDivElement|NodeListOf}
+ */
+ static parseDom(domStr, withWrapper, className) {
+ withWrapper = withWrapper ?? false
+ let el = document.createElement('div')
+ el.className = className || ''
+ el.innerHTML = domStr
+ return withWrapper ? el : el.childNodes
+ }
+
+ /**
+ * enter full screen
+ * @param el
+ */
+ static enterFullscreen(el) {
+ if (!el) {
+ return
+ }
+ if (el.requestFullscreen) {
+ el.requestFullscreen()
+ } else if (el.msRequestFullscreen) {
+ el.msRequestFullscreen()
+ } else if (el.mozRequestFullScreen) {
+ el.mozRequestFullScreen()
+ } else if (el.webkitRequestFullscreen) {
+ el.webkitRequestFullscreen()
+ }
+ }
+
+ /**
+ * exit full screen
+ */
+ static exitFullscreen() {
+ if (document.exitFullscreen) {
+ document.exitFullscreen()
+ } else if (document.msExitFullscreen) {
+ document.msExitFullscreen()
+ } else if (document.mozCancelFullScreen) {
+ document.mozCancelFullScreen()
+ } else if (document.webkitExitFullscreen) {
+ document.webkitExitFullscreen()
+ }
+ }
+
+ /**
+ * Creates video
+ * @param url
+ * @param className
+ * @param container
+ * @returns {HTMLElement}
+ */
+ static createVideo(url, className, container = null) {
+ let videoEl = this.create('video', className, container)
+ let map4 = this.create('source', '', videoEl)
+ map4.setAttribute('src', url)
+ map4.setAttribute('type', 'video/map4')
+ let mov = this.create('source', '', videoEl)
+ mov.setAttribute('src', url)
+ mov.setAttribute('type', 'video/quicktime')
+ return videoEl
+ }
+}
+
+export default DomUtil
diff --git a/src/modules/utils/PlotUtil.js b/src/modules/utils/PlotUtil.js
new file mode 100644
index 00000000..3cb2d257
--- /dev/null
+++ b/src/modules/utils/PlotUtil.js
@@ -0,0 +1,496 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-08-21 18:05:39
+ */
+
+const TWO_PI = Math.PI * 2
+const FITTING_COUNT = 100
+const ZERO_TOLERANCE = 0.0001
+
+class PlotUtil {
+ /**
+ * @param pnt1
+ * @param pnt2
+ * @returns {number}
+ */
+ static distance(pnt1, pnt2) {
+ return Math.sqrt(
+ Math.pow(pnt1[0] - pnt2[0], 2) + Math.pow(pnt1[1] - pnt2[1], 2)
+ )
+ }
+
+ /**
+ * @param points
+ * @returns {number}
+ */
+ static wholeDistance(points) {
+ let distance = 0
+ for (let i = 0; i < points.length - 1; i++)
+ distance += this.distance(points[i], points[i + 1])
+ return distance
+ }
+
+ /**
+ * @param points
+ * @returns {number}
+ */
+ static getBaseLength(points) {
+ return Math.pow(this.wholeDistance(points), 0.99)
+ }
+
+ /**
+ * @param pnt1
+ * @param pnt2
+ * @returns {number[]}
+ */
+ static mid(pnt1, pnt2) {
+ return [(pnt1[0] + pnt2[0]) / 2, (pnt1[1] + pnt2[1]) / 2]
+ }
+
+ /**
+ * @param pnt1
+ * @param pnt2
+ * @param pnt3
+ * @returns {[*, *]|[*, *]|[*, number]}
+ */
+ static getCircleCenterOfThreePoints(pnt1, pnt2, pnt3) {
+ let pntA = [(pnt1[0] + pnt2[0]) / 2, (pnt1[1] + pnt2[1]) / 2]
+ let pntB = [pntA[0] - pnt1[1] + pnt2[1], pntA[1] + pnt1[0] - pnt2[0]]
+ let pntC = [(pnt1[0] + pnt3[0]) / 2, (pnt1[1] + pnt3[1]) / 2]
+ let pntD = [pntC[0] - pnt1[1] + pnt3[1], pntC[1] + pnt1[0] - pnt3[0]]
+ return this.getIntersectPoint(pntA, pntB, pntC, pntD)
+ }
+
+ /**
+ * @param pntA
+ * @param pntB
+ * @param pntC
+ * @param pntD
+ * @returns {(*|number)[]|*[]}
+ */
+ static getIntersectPoint(pntA, pntB, pntC, pntD) {
+ let x, y, f, e
+ if (pntA[1] === pntB[1]) {
+ f = (pntD[0] - pntC[0]) / (pntD[1] - pntC[1])
+ x = f * (pntA[1] - pntC[1]) + pntC[0]
+ y = pntA[1]
+ return [x, y]
+ }
+ if (pntC[1] === pntD[1]) {
+ e = (pntB[0] - pntA[0]) / (pntB[1] - pntA[1])
+ x = e * (pntC[1] - pntA[1]) + pntA[0]
+ y = pntC[1]
+ return [x, y]
+ }
+ e = (pntB[0] - pntA[0]) / (pntB[1] - pntA[1])
+ f = (pntD[0] - pntC[0]) / (pntD[1] - pntC[1])
+ y = (e * pntA[1] - pntA[0] - f * pntC[1] + pntC[0]) / (e - f)
+ x = e * y - e * pntA[1] + pntA[0]
+ return [x, y]
+ }
+
+ /**
+ * @param startPnt
+ * @param endPnt
+ * @returns {number}
+ */
+ static getAzimuth(startPnt, endPnt) {
+ let azimuth
+ let angle = Math.asin(
+ Math.abs(endPnt[1] - startPnt[1]) / this.distance(startPnt, endPnt)
+ )
+ if (endPnt[1] >= startPnt[1] && endPnt[0] >= startPnt[0])
+ azimuth = angle + Math.PI
+ else if (endPnt[1] >= startPnt[1] && endPnt[0] < startPnt[0])
+ azimuth = TWO_PI - angle
+ else if (endPnt[1] < startPnt[1] && endPnt[0] < startPnt[0]) azimuth = angle
+ else if (endPnt[1] < startPnt[1] && endPnt[0] >= startPnt[0])
+ azimuth = Math.PI - angle
+ return azimuth
+ }
+
+ /**
+ * @param pntA
+ * @param pntB
+ * @param pntC
+ * @returns {number}
+ */
+ static getAngleOfThreePoints(pntA, pntB, pntC) {
+ let angle = this.getAzimuth(pntB, pntA) - this.getAzimuth(pntB, pntC)
+ return angle < 0 ? angle + TWO_PI : angle
+ }
+
+ /**
+ * @param pnt1
+ * @param pnt2
+ * @param pnt3
+ * @returns {boolean}
+ */
+ static isClockWise(pnt1, pnt2, pnt3) {
+ return (
+ (pnt3[1] - pnt1[1]) * (pnt2[0] - pnt1[0]) >
+ (pnt2[1] - pnt1[1]) * (pnt3[0] - pnt1[0])
+ )
+ }
+
+ /**
+ * @param t
+ * @param startPnt
+ * @param endPnt
+ * @returns {*[]}
+ */
+ static getPointOnLine(t, startPnt, endPnt) {
+ let x = startPnt[0] + t * (endPnt[0] - startPnt[0])
+ let y = startPnt[1] + t * (endPnt[1] - startPnt[1])
+ return [x, y]
+ }
+
+ /**
+ * @param t
+ * @param startPnt
+ * @param cPnt1
+ * @param cPnt2
+ * @param endPnt
+ * @returns {number[]}
+ */
+ static getCubicValue(t, startPnt, cPnt1, cPnt2, endPnt) {
+ t = Math.max(Math.min(t, 1), 0)
+ let tp = 1 - t
+ let t2 = t * t
+ let t3 = t2 * t
+ let tp2 = tp * tp
+ let tp3 = tp2 * tp
+ let x =
+ tp3 * startPnt[0] +
+ 3 * tp2 * t * cPnt1[0] +
+ 3 * tp * t2 * cPnt2[0] +
+ t3 * endPnt[0]
+ let y =
+ tp3 * startPnt[1] +
+ 3 * tp2 * t * cPnt1[1] +
+ 3 * tp * t2 * cPnt2[1] +
+ t3 * endPnt[1]
+ return [x, y]
+ }
+
+ /**
+ * @param startPnt
+ * @param endPnt
+ * @param angle
+ * @param distance
+ * @param clockWise
+ * @returns {*[]}
+ */
+ static getThirdPoint(startPnt, endPnt, angle, distance, clockWise) {
+ let azimuth = this.getAzimuth(startPnt, endPnt)
+ let alpha = clockWise ? azimuth + angle : azimuth - angle
+ let dx = distance * Math.cos(alpha)
+ let dy = distance * Math.sin(alpha)
+ return [endPnt[0] + dx, endPnt[1] + dy]
+ }
+
+ /**
+ * @param center
+ * @param radius
+ * @param startAngle
+ * @param endAngle
+ * @returns {[]}
+ */
+ static getArcPoints(center, radius, startAngle, endAngle) {
+ let x,
+ y,
+ pnts = []
+ let angleDiff = endAngle - startAngle
+ angleDiff = angleDiff < 0 ? angleDiff + TWO_PI : angleDiff
+ for (let i = 0; i <= FITTING_COUNT; i++) {
+ let angle = startAngle + (angleDiff * i) / FITTING_COUNT
+ x = center[0] + radius * Math.cos(angle)
+ y = center[1] + radius * Math.sin(angle)
+ pnts.push([x, y])
+ }
+ return pnts
+ }
+
+ /**
+ * @param t
+ * @param pnt1
+ * @param pnt2
+ * @param pnt3
+ * @returns {*[][]}
+ */
+ static getBisectorNormals(t, pnt1, pnt2, pnt3) {
+ let normal = this.getNormal(pnt1, pnt2, pnt3)
+ let dist = Math.sqrt(normal[0] * normal[0] + normal[1] * normal[1])
+ let uX = normal[0] / dist
+ let uY = normal[1] / dist
+ let d1 = this.distance(pnt1, pnt2)
+ let d2 = this.distance(pnt2, pnt3)
+ let dt, x, y, bisectorNormalLeft, bisectorNormalRight
+ if (dist > ZERO_TOLERANCE) {
+ if (this.isClockWise(pnt1, pnt2, pnt3)) {
+ dt = t * d1
+ x = pnt2[0] - dt * uY
+ y = pnt2[1] + dt * uX
+ bisectorNormalRight = [x, y]
+ dt = t * d2
+ x = pnt2[0] + dt * uY
+ y = pnt2[1] - dt * uX
+ bisectorNormalLeft = [x, y]
+ } else {
+ dt = t * d1
+ x = pnt2[0] + dt * uY
+ y = pnt2[1] - dt * uX
+ bisectorNormalRight = [x, y]
+ dt = t * d2
+ x = pnt2[0] - dt * uY
+ y = pnt2[1] + dt * uX
+ bisectorNormalLeft = [x, y]
+ }
+ } else {
+ x = pnt2[0] + t * (pnt1[0] - pnt2[0])
+ y = pnt2[1] + t * (pnt1[1] - pnt2[1])
+ bisectorNormalRight = [x, y]
+ x = pnt2[0] + t * (pnt3[0] - pnt2[0])
+ y = pnt2[1] + t * (pnt3[1] - pnt2[1])
+ bisectorNormalLeft = [x, y]
+ }
+ return [bisectorNormalRight, bisectorNormalLeft]
+ }
+
+ /**
+ * @param pnt1
+ * @param pnt2
+ * @param pnt3
+ * @returns {number[]}
+ */
+ static getNormal(pnt1, pnt2, pnt3) {
+ let dX1 = pnt1[0] - pnt2[0]
+ let dY1 = pnt1[1] - pnt2[1]
+ let d1 = Math.sqrt(dX1 * dX1 + dY1 * dY1)
+ dX1 /= d1
+ dY1 /= d1
+
+ let dX2 = pnt3[0] - pnt2[0]
+ let dY2 = pnt3[1] - pnt2[1]
+ let d2 = Math.sqrt(dX2 * dX2 + dY2 * dY2)
+ dX2 /= d2
+ dY2 /= d2
+
+ let uX = dX1 + dX2
+ let uY = dY1 + dY2
+ return [uX, uY]
+ }
+
+ /**
+ * @param t
+ * @param controlPoints
+ * @returns {[]}
+ */
+ static getCurvePoints(t, controlPoints) {
+ let leftControl = this.getLeftMostControlPoint(t, controlPoints)
+ let normals = [leftControl]
+ let pnt1, pnt2, pnt3, normalPoints
+ for (let i = 0; i < controlPoints.length - 2; i++) {
+ pnt1 = controlPoints[i]
+ pnt2 = controlPoints[i + 1]
+ pnt3 = controlPoints[i + 2]
+ normalPoints = this.getBisectorNormals(t, pnt1, pnt2, pnt3)
+ normals = normals.concat(normalPoints)
+ }
+ let rightControl = this.getRightMostControlPoint(t, controlPoints)
+ normals.push(rightControl)
+ let points = []
+ for (let i = 0; i < controlPoints.length - 1; i++) {
+ pnt1 = controlPoints[i]
+ pnt2 = controlPoints[i + 1]
+ points.push(pnt1)
+ for (let t = 0; t < FITTING_COUNT; t++) {
+ let pnt = this.getCubicValue(
+ t / FITTING_COUNT,
+ pnt1,
+ normals[i * 2],
+ normals[i * 2 + 1],
+ pnt2
+ )
+ points.push(pnt)
+ }
+ points.push(pnt2)
+ }
+ return points
+ }
+
+ /**
+ * @param t
+ * @param controlPoints
+ * @returns {number[]}
+ */
+ static getLeftMostControlPoint(t, controlPoints) {
+ let pnt1 = controlPoints[0]
+ let pnt2 = controlPoints[1]
+ let pnt3 = controlPoints[2]
+ let pnts = this.getBisectorNormals(0, pnt1, pnt2, pnt3)
+ let normalRight = pnts[0]
+ let normal = this.getNormal(pnt1, pnt2, pnt3)
+ let dist = Math.sqrt(normal[0] * normal[0] + normal[1] * normal[1])
+ let controlX, controlY
+ if (dist > ZERO_TOLERANCE) {
+ let mid = this.mid(pnt1, pnt2)
+ let pX = pnt1[0] - mid[0]
+ let pY = pnt1[1] - mid[1]
+ let d1 = this.distance(pnt1, pnt2)
+ // normal at midpoint
+ let n = 2.0 / d1
+ let nX = -n * pY
+ let nY = n * pX
+ // upper triangle of symmetric transform matrix
+ let a11 = nX * nX - nY * nY
+ let a12 = 2 * nX * nY
+ let a22 = nY * nY - nX * nX
+ let dX = normalRight[0] - mid[0]
+ let dY = normalRight[1] - mid[1]
+ // coordinates of reflected vector
+ controlX = mid[0] + a11 * dX + a12 * dY
+ controlY = mid[1] + a12 * dX + a22 * dY
+ } else {
+ controlX = pnt1[0] + t * (pnt2[0] - pnt1[0])
+ controlY = pnt1[1] + t * (pnt2[1] - pnt1[1])
+ }
+ return [controlX, controlY]
+ }
+
+ /**
+ * @param t
+ * @param controlPoints
+ * @returns {number[]}
+ */
+ static getRightMostControlPoint(t, controlPoints) {
+ let count = controlPoints.length
+ let pnt1 = controlPoints[count - 3]
+ let pnt2 = controlPoints[count - 2]
+ let pnt3 = controlPoints[count - 1]
+ let pnts = this.getBisectorNormals(0, pnt1, pnt2, pnt3)
+ let normalLeft = pnts[1]
+ let normal = this.getNormal(pnt1, pnt2, pnt3)
+ let dist = Math.sqrt(normal[0] * normal[0] + normal[1] * normal[1])
+ let controlX, controlY
+ if (dist > ZERO_TOLERANCE) {
+ let mid = this.mid(pnt2, pnt3)
+ let pX = pnt3[0] - mid[0]
+ let pY = pnt3[1] - mid[1]
+
+ let d1 = this.distance(pnt2, pnt3)
+ // normal at midpoint
+ let n = 2.0 / d1
+ let nX = -n * pY
+ let nY = n * pX
+
+ // upper triangle of symmetric transform matrix
+ let a11 = nX * nX - nY * nY
+ let a12 = 2 * nX * nY
+ let a22 = nY * nY - nX * nX
+
+ let dX = normalLeft[0] - mid[0]
+ let dY = normalLeft[1] - mid[1]
+
+ // coordinates of reflected vector
+ controlX = mid[0] + a11 * dX + a12 * dY
+ controlY = mid[1] + a12 * dX + a22 * dY
+ } else {
+ controlX = pnt3[0] + t * (pnt2[0] - pnt3[0])
+ controlY = pnt3[1] + t * (pnt2[1] - pnt3[1])
+ }
+ return [controlX, controlY]
+ }
+
+ /**
+ * @param points
+ * @returns {[]|*}
+ */
+ static getBezierPoints(points) {
+ if (points.length <= 2) return points
+ let bezierPoints = []
+ let n = points.length - 1
+ for (let t = 0; t <= 1; t += 0.01) {
+ let x = 0
+ let y = 0
+ for (let index = 0; index <= n; index++) {
+ let factor = this.getBinomialFactor(n, index)
+ let a = Math.pow(t, index)
+ let b = Math.pow(1 - t, n - index)
+ x += factor * a * b * points[index][0]
+ y += factor * a * b * points[index][1]
+ }
+ bezierPoints.push([x, y])
+ }
+ bezierPoints.push(points[n])
+ return bezierPoints
+ }
+
+ /**
+ *
+ * @param n
+ * @param index
+ * @returns {number}
+ */
+ static getBinomialFactor(n, index) {
+ return (
+ this.getFactorial(n) /
+ (this.getFactorial(index) * this.getFactorial(n - index))
+ )
+ }
+
+ /**
+ * @param n
+ * @returns {number}
+ */
+ static getFactorial(n) {
+ if (n <= 1) return 1
+ if (n === 2) return 2
+ if (n === 3) return 6
+ if (n === 4) return 24
+ if (n === 5) return 120
+ let result = 1
+ for (let i = 1; i <= n; i++) result *= i
+ return result
+ }
+
+ /**
+ * @param points
+ * @returns {[]|*}
+ */
+ static getQBSplinePoints(points) {
+ if (points.length <= 2) return points
+ let n = 2
+ let bSplinePoints = []
+ let m = points.length - n - 1
+ bSplinePoints.push(points[0])
+ for (let i = 0; i <= m; i++) {
+ for (let t = 0; t <= 1; t += 0.05) {
+ let x = 0
+ let y = 0
+ for (let k = 0; k <= n; k++) {
+ let factor = this.getQuadricBSplineFactor(k, t)
+ x += factor * points[i + k][0]
+ y += factor * points[i + k][1]
+ }
+ bSplinePoints.push([x, y])
+ }
+ }
+ bSplinePoints.push(points[points.length - 1])
+ return bSplinePoints
+ }
+
+ /**
+ * @param k
+ * @param t
+ * @returns {number}
+ */
+ static getQuadricBSplineFactor(k, t) {
+ if (k === 0) return Math.pow(t - 1, 2) / 2
+ if (k === 1) return (-2 * Math.pow(t, 2) + 2 * t + 1) / 2
+ if (k === 2) return Math.pow(t, 2) / 2
+ return 0
+ }
+}
+
+export default PlotUtil
diff --git a/src/modules/utils/Util.js b/src/modules/utils/Util.js
new file mode 100644
index 00000000..1bc9b82c
--- /dev/null
+++ b/src/modules/utils/Util.js
@@ -0,0 +1,175 @@
+/**
+ * @Author: Caven
+ * @Date: 2019-12-31 17:58:01
+ */
+
+const CHARS =
+ '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('')
+
+/**
+ * Some of the code borrows from leaflet
+ * https://github.com/Leaflet/Leaflet/tree/master/src/core
+ */
+class Util {
+ /**
+ * Generates uuid
+ * @param prefix
+ * @returns {string}
+ */
+ static uuid(prefix = 'D') {
+ let uuid = []
+ uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'
+ uuid[14] = '4'
+ let r
+ for (let i = 0; i < 36; i++) {
+ if (!uuid[i]) {
+ r = 0 | (Math.random() * 16)
+ uuid[i] = CHARS[i === 19 ? (r & 0x3) | 0x8 : r]
+ }
+ }
+ return prefix + '-' + uuid.join('')
+ }
+
+ /**
+
+ * Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter.
+ * @param dest
+ * @param sources
+ * @returns {*}
+ */
+ static merge(dest, ...sources) {
+ let i, j, len, src
+ for (j = 0, len = sources.length; j < len; j++) {
+ src = sources[j]
+ for (i in src) {
+ dest[i] = src[i]
+ }
+ }
+ return dest
+ }
+
+ /**
+ * @function splitWords(str: String): String[]
+ * Trims and splits the string on whitespace and returns the array of parts.
+ * @param {*} str
+ */
+ static splitWords(str) {
+ return this.trim(str).split(/\s+/)
+ }
+
+ /**
+ * @function setOptions(obj: Object, options: Object): Object
+ * Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`.
+ * @param {*} obj
+ * @param {*} options
+ */
+ static setOptions(obj, options) {
+ if (!obj.hasOwnProperty('options')) {
+ obj.options = obj.options ? Object.create(obj.options) : {}
+ }
+ for (let i in options) {
+ obj.options[i] = options[i]
+ }
+ return obj.options
+ }
+
+ /**
+ * @function formatNum(num: Number, digits?: Number): Number
+ * Returns the number `num` rounded to `digits` decimals, or to 6 decimals by default.
+ * @param num
+ * @param digits
+ * @returns {number}
+ */
+ static formatNum(num, digits) {
+ let pow = Math.pow(10, digits === undefined ? 6 : digits)
+ return Math.round(num * pow) / pow
+ }
+
+ /**
+ * @function trim(str: String): String
+ * Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim)
+ * @param {*} str
+ */
+ static trim(str) {
+ return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '')
+ }
+
+ /**
+ * Data URI string containing a base64-encoded empty GIF image.
+ * Used as a hack to free memory from unused images on WebKit-powered
+ * mobile devices (by setting image `src` to this string).
+ * @returns {string}
+ */
+ static emptyImageUrl() {
+ return (function () {
+ return 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='
+ })()
+ }
+
+ /**
+ * @function checkPosition(position: Object): Boolean
+ * Check position for validity
+ * @param {*} position
+ */
+ static checkPosition(position) {
+ return (
+ position &&
+ position.hasOwnProperty('_lng') &&
+ position.hasOwnProperty('_lat') &&
+ position.hasOwnProperty('_alt')
+ )
+ }
+
+ /**
+ * Creates a debounced function that delays invoking `fn` until after `delay`
+ * @param fn
+ * @param delay
+ * @returns {function(): void}
+ */
+ static debounce(fn, delay) {
+ let timer = null
+ return function () {
+ timer && clearTimeout(timer)
+ timer = setTimeout(fn, delay)
+ }
+ }
+
+ /**
+ * Creates a throttled function that only invokes `fn` at most once per
+ * @param fn
+ * @param delay
+ * @returns {function(): void}
+ */
+ static throttle(fn, delay) {
+ let valid = true
+ return function () {
+ if (!valid) {
+ return false
+ }
+ valid = false
+ setTimeout(() => {
+ fn()
+ valid = true
+ }, delay)
+ }
+ }
+
+ /**
+ *
+ * @param dataUrl
+ * @returns {Blob}
+ */
+ static dataURLtoBlob(dataUrl) {
+ let arr = dataUrl.split(',')
+ let mime = arr[0].match(/:(.*?);/)[1]
+ let bStr = atob(arr[1])
+ let len = bStr.length
+ let u8Arr = new Uint8Array(len)
+ while (len--) {
+ u8Arr[len] = bStr.charCodeAt(len)
+ }
+ return new Blob([u8Arr], { type: mime })
+ }
+}
+
+export default Util
diff --git a/src/modules/utils/index.js b/src/modules/utils/index.js
new file mode 100644
index 00000000..4b9dfa4f
--- /dev/null
+++ b/src/modules/utils/index.js
@@ -0,0 +1,7 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-06 16:38:49
+ */
+export { default as Util } from './Util'
+export { default as DomUtil } from './DomUtil'
+export { default as PlotUtil } from './PlotUtil'
diff --git a/src/modules/viewer/Viewer.js b/src/modules/viewer/Viewer.js
new file mode 100644
index 00000000..bfbab7e5
--- /dev/null
+++ b/src/modules/viewer/Viewer.js
@@ -0,0 +1,659 @@
+/**
+ * @Author: Caven
+ * @Date: 2019-12-27 17:13:24
+ */
+
+import { Cesium } from '../../namespace'
+import Parse from '../parse/Parse'
+import {
+ LayerGroupEventType,
+ LayerEventType,
+ MouseEvent,
+ ViewerEvent,
+ SceneEvent,
+} from '../event'
+import { ViewerOption, CameraOption } from '../option'
+import { Util, DomUtil } from '../utils'
+import { Transform } from '../transform'
+import createWidgets from '../widget'
+import createTools from '../tools'
+import { BaseLayerPicker, CesiumViewer } from '../exts'
+
+const DEF_OPTS = {
+ creditContainer: undefined,
+ shouldAnimate: true,
+}
+
+class Viewer {
+ constructor(id, options = {}) {
+ if (!id || (typeof id === 'string' && !document.getElementById(id))) {
+ throw new Error('Viewer:the id is empty')
+ }
+ this._delegate = Cesium.Viewer
+ ? Cesium.Viewer(id, {
+ ...DEF_OPTS,
+ ...options,
+ })
+ : new CesiumViewer(id, {
+ ...DEF_OPTS,
+ ...options,
+ }) // Initialize the viewer
+
+ /**
+ * Registers events
+ */
+ new MouseEvent(this) // Register global mouse events
+ this._viewerEvent = new ViewerEvent() // Register viewer events
+ this._sceneEvent = new SceneEvent(this) // Register scene events
+
+ this._viewerOption = new ViewerOption(this) // Initialize the viewer option
+ this._cameraOption = new CameraOption(this) // Initialize the camera option
+
+ this._dcContainer = DomUtil.create(
+ 'div',
+ 'dc-container',
+ typeof id === 'string' ? document.getElementById(id) : id
+ ) //Register the custom container
+
+ this._baseLayerPicker = new BaseLayerPicker({
+ globe: this._delegate.scene.globe,
+ }) //Initialize the baseLayer picker
+
+ this._layerGroupCache = {}
+ this._layerCache = {}
+
+ /**
+ * Registers default widgets
+ */
+ let widgets = createWidgets()
+ Object.keys(widgets).forEach((key) => {
+ this.use(widgets[key])
+ })
+
+ /**
+ * Registers default tools
+ */
+ let tools = createTools()
+ Object.keys(tools).forEach((key) => {
+ this.use(tools[key])
+ })
+ }
+
+ get delegate() {
+ return this._delegate
+ }
+
+ get dcContainer() {
+ return this._dcContainer
+ }
+
+ get scene() {
+ return this._delegate.scene
+ }
+
+ get camera() {
+ return this._delegate.camera
+ }
+
+ get canvas() {
+ return this._delegate.scene.canvas
+ }
+
+ get dataSources() {
+ return this._delegate.dataSources
+ }
+
+ get imageryLayers() {
+ return this._delegate.imageryLayers
+ }
+
+ get terrainProvider() {
+ return this._delegate.terrainProvider
+ }
+
+ get entities() {
+ return this._delegate.entities
+ }
+
+ get postProcessStages() {
+ return this._delegate.scene.postProcessStages
+ }
+
+ get clock() {
+ return this._delegate.clock
+ }
+
+ get viewerEvent() {
+ return this._viewerEvent
+ }
+
+ get cameraPosition() {
+ let position = Transform.transformCartesianToWGS84(this.camera.positionWC)
+ if (position) {
+ position.heading = Cesium.Math.toDegrees(this.camera.heading)
+ position.pitch = Cesium.Math.toDegrees(this.camera.pitch)
+ position.roll = Cesium.Math.toDegrees(this.camera.roll)
+ }
+ return position
+ }
+
+ get resolution() {
+ let width = this.scene.canvas.width
+ let height = this.scene.canvas.height
+ let min = Transform.transformWindowToWGS84(
+ new Cesium.Cartesian2((width / 2) | 0, height - 1),
+ this
+ )
+ let max = Transform.transformWindowToWGS84(
+ new Cesium.Cartesian2((1 + width / 2) | 0, height - 1),
+ this
+ )
+ if (!min || !max) {
+ return 1
+ }
+ return Math.abs(min.lng - max.lng)
+ }
+
+ get viewBounds() {
+ let width = this.scene.canvas.width
+ let height = this.scene.canvas.height
+ let min = Transform.transformWindowToWGS84(
+ new Cesium.Cartesian2(0, height),
+ this
+ )
+ let max = Transform.transformWindowToWGS84(
+ new Cesium.Cartesian2(width, 0),
+ this
+ )
+ if (!min || !max) {
+ return Cesium.Rectangle.MAX_VALUE
+ }
+ return Cesium.Rectangle.fromDegrees(min.lng, min.lat, max.lng, max.lat)
+ }
+
+ get level() {
+ return this._delegate.scene.globe._surface._debug.maxDepthVisited
+ }
+
+ /***
+ *
+ * @param layerGroup
+ * @private
+ */
+ _addLayerGroup(layerGroup) {
+ if (
+ layerGroup?.layerGroupEvent &&
+ // eslint-disable-next-line no-prototype-builtins
+ !Object(this._layerGroupCache).hasOwnProperty(layerGroup.id)
+ ) {
+ layerGroup.layerGroupEvent.fire(LayerGroupEventType.ADD, this)
+ this._layerGroupCache[layerGroup.id] = layerGroup
+ }
+ }
+
+ /**
+ *
+ * @param layerGroup
+ * @private
+ */
+ _removeLayerGroup(layerGroup) {
+ if (
+ layerGroup?.layerGroupEvent &&
+ // eslint-disable-next-line no-prototype-builtins
+ Object(this._layerGroupCache).hasOwnProperty(layerGroup.id)
+ ) {
+ layerGroup.layerGroupEvent.fire(LayerGroupEventType.REMOVE, this)
+ delete this._layerGroupCache[layerGroup.id]
+ }
+ }
+
+ /**
+ * @param layer
+ * @private
+ */
+ _addLayer(layer) {
+ !this._layerCache[layer.type] && (this._layerCache[layer.type] = {})
+ // eslint-disable-next-line no-prototype-builtins
+ if (!Object(this._layerCache[layer.type]).hasOwnProperty(layer.id)) {
+ layer.fire(LayerEventType.ADD, this)
+ this._layerCache[layer.type][layer.id] = layer
+ }
+ }
+
+ /**
+ * @param layer
+ * @private
+ */
+ _removeLayer(layer) {
+ // eslint-disable-next-line no-prototype-builtins
+ if (Object(this._layerCache[layer.type]).hasOwnProperty(layer.id)) {
+ layer.fire(LayerEventType.REMOVE, this)
+ delete this._layerCache[layer.type][layer.id]
+ }
+ }
+
+ /**
+ * Sets viewer options
+ * @param options
+ * @returns {Viewer}
+ */
+ setOptions(options) {
+ this._viewerOption.setOptions(options)
+ return this
+ }
+
+ /**
+ * Sets camera pitch range
+ * @param min
+ * @param max
+ * @returns {Viewer}
+ */
+ setPitchRange(min = -90, max = -20) {
+ this._cameraOption.setPitchRange(min, max)
+ return this
+ }
+
+ /**
+ * Changes Scene Mode,2:2D,2.5:2.5D,3:3D
+ * @param sceneMode
+ * @param duration
+ * @returns {Viewer}
+ */
+ changeSceneMode(sceneMode, duration = 0) {
+ if (sceneMode === 2) {
+ this._delegate.scene.morphTo2D(duration)
+ } else if (sceneMode === 3) {
+ this._delegate.scene.morphTo3D(duration)
+ } else if (sceneMode === 2.5) {
+ this._delegate.scene.morphToColumbusView(duration)
+ }
+ return this
+ }
+
+ /**
+ * Changes Mouse Mode,0:Default,1: Change the tiltEventTypes to CameraEventType.RIGHT_DRAG
+ * @param mouseMode
+ * @returns {Viewer}
+ */
+ changeMouseMode(mouseMode) {
+ this._cameraOption.changeMouseMode(mouseMode)
+ return this
+ }
+
+ /**
+ * Adds the baseLayer .
+ * The baseLayer can be a single or an array,
+ * and when the baseLayer is an array, the baseLayer will be loaded together
+ * @param baseLayers
+ * @param options
+ * @returns {Viewer}
+ */
+ addBaseLayer(baseLayers, options = {}) {
+ if (!baseLayers) {
+ return this
+ }
+ this._baseLayerPicker.addImageryProvider(baseLayers)
+ if (!this._baseLayerPicker.selectedImagery) {
+ this._baseLayerPicker.changeImagery(0)
+ }
+ this.mapSwitch && this.mapSwitch.addMap(options)
+ return this
+ }
+
+ /**
+ * Changes the current globe display of the baseLayer
+ * @param index
+ * @returns {Viewer}
+ */
+ changeBaseLayer(index) {
+ this._baseLayerPicker.changeImagery(index)
+ return this
+ }
+
+ /**
+ *
+ * @param windowPosition
+ * @returns {Promise}
+ */
+ getImageryLayerInfo(windowPosition) {
+ let ray = this._delegate.camera.getPickRay(windowPosition)
+ return this._delegate.imageryLayers.pickImageryLayerFeatures(
+ ray,
+ this._delegate.scene
+ )
+ }
+
+ /**
+ *
+ * @param terrain
+ * @return {Viewer}
+ */
+ addTerrain(terrain) {
+ if (!terrain) {
+ return this
+ }
+ this._baseLayerPicker.addTerrainProvider(terrain)
+ if (!this._baseLayerPicker.selectedTerrain) {
+ this._baseLayerPicker.changeTerrain(0)
+ }
+ return this
+ }
+
+ /**
+ * Changes the current globe display of the terrain
+ * @param index
+ * @returns {Viewer}
+ */
+ changeTerrain(index) {
+ this._baseLayerPicker.changeTerrain(index)
+ return this
+ }
+
+ /**
+ * Removes terrain
+ * @returns {Viewer}
+ */
+ removeTerrain() {
+ this._delegate.terrainProvider = new Cesium.EllipsoidTerrainProvider()
+ return this
+ }
+
+ /**
+ *
+ * @param layerGroup
+ * @returns {Viewer}
+ */
+ addLayerGroup(layerGroup) {
+ this._addLayerGroup(layerGroup)
+ return this
+ }
+
+ /**
+ *
+ * @param layerGroup
+ * @returns {Viewer}
+ */
+ removeLayerGroup(layerGroup) {
+ this._removeLayerGroup(layerGroup)
+ return this
+ }
+
+ /**
+ *
+ * @param id
+ * @returns {undefined}
+ */
+ getLayerGroup(id) {
+ return this._layerGroupCache[id] || undefined
+ }
+
+ /**
+ * add a layer
+ * @param layer
+ * @returns {Viewer}
+ */
+ addLayer(layer) {
+ this._addLayer(layer)
+ return this
+ }
+
+ /**
+ * Removes a layer
+ * @param layer
+ * @returns {Viewer}
+ */
+ removeLayer(layer) {
+ this._removeLayer(layer)
+ return this
+ }
+
+ /**
+ * Checks to see if the layer is included
+ * @param layer
+ * @returns {boolean}
+ */
+ hasLayer(layer) {
+ // eslint-disable-next-line no-prototype-builtins
+ return Object(this._layerCache[layer.type]).hasOwnProperty(layer.id)
+ }
+
+ /**
+ * Returns a layer by id
+ * @param id
+ * @returns {*|undefined}
+ */
+ getLayer(id) {
+ let filters = this.getLayers().filter((item) => item.id === id)
+ return filters && filters.length ? filters[0] : undefined
+ }
+
+ /**
+ * Returns all layers
+ * @returns {[]}
+ */
+ getLayers() {
+ let result = []
+ Object.keys(this._layerCache).forEach((type) => {
+ let cache = this._layerCache[type]
+ Object.keys(cache).forEach((layerId) => {
+ result.push(cache[layerId])
+ })
+ })
+ return result
+ }
+
+ /**
+ * Iterate through each layer and pass it as an argument to the callback function
+ * @param method
+ * @param context
+ * @returns {Viewer}
+ */
+ eachLayer(method, context) {
+ Object.keys(this._layerCache).forEach((type) => {
+ let cache = this._layerCache[type]
+ Object.keys(cache).forEach((layerId) => {
+ method.call(context, cache[layerId])
+ })
+ })
+ return this
+ }
+
+ /**
+ * @param target
+ * @param duration
+ * @returns {Viewer}
+ */
+ flyTo(target, duration) {
+ this._delegate.flyTo(target?.delegate || target, {
+ duration,
+ })
+ return this
+ }
+
+ /**
+ * @param target
+ * @returns {Viewer}
+ */
+ zoomTo(target) {
+ this._delegate.zoomTo(target?.delegate || target)
+ return this
+ }
+
+ /**
+ * Camera fly to a position
+ * @param position
+ * @param completeCallback
+ * @param duration
+ * @returns {Viewer}
+ */
+ flyToPosition(position, completeCallback, duration) {
+ position = Parse.parsePosition(position)
+ this.camera.flyTo({
+ destination: Transform.transformWGS84ToCartesian(position),
+ orientation: {
+ heading: Cesium.Math.toRadians(position.heading),
+ pitch: Cesium.Math.toRadians(position.pitch),
+ roll: Cesium.Math.toRadians(position.roll),
+ },
+ complete: completeCallback,
+ duration: duration,
+ })
+ return this
+ }
+
+ /**
+ * Camera zoom to a position
+ * @param position
+ * @param completeCallback
+ * @returns {Viewer}
+ */
+ zoomToPosition(position, completeCallback) {
+ this.flyToPosition(position, completeCallback, 0)
+ return this
+ }
+
+ /**
+ * Camera fly to bounds
+ * @param bounds
+ * @param heading
+ * @param pitch
+ * @param roll
+ * @param completeCallback
+ * @param duration
+ * @return {Viewer}
+ */
+ flyToBounds(
+ bounds,
+ { heading = 0, pitch = 0, roll = 0 },
+ completeCallback,
+ duration
+ ) {
+ if (!bounds) {
+ return this
+ }
+ if (!Array.isArray(bounds)) {
+ bounds = bounds.split(',')
+ }
+ this.camera.flyTo({
+ destination: Cesium.Rectangle.fromDegrees(
+ bounds[0],
+ bounds[1],
+ bounds[2],
+ bounds[3]
+ ),
+ orientation: {
+ heading: Cesium.Math.toRadians(heading),
+ pitch: Cesium.Math.toRadians(pitch),
+ roll: Cesium.Math.toRadians(roll),
+ },
+ complete: completeCallback,
+ duration: duration,
+ })
+ return this
+ }
+
+ /**
+ *
+ * @param bounds
+ * @param heading
+ * @param pitch
+ * @param roll
+ * @param completeCallback
+ * @return {Viewer}
+ */
+ zoomToBounds(bounds, { heading = 0, pitch = 0, roll = 0 }, completeCallback) {
+ this.flyToBounds(bounds, { heading, pitch, roll }, completeCallback)
+ return this
+ }
+
+ /**
+ *
+ * @param type
+ * @param callback
+ * @param context
+ * @returns {Viewer}
+ */
+ on(type, callback, context) {
+ this._viewerEvent.on(type, callback, context || this)
+ this._sceneEvent.on(type, callback, context || this)
+ return this
+ }
+
+ /**
+ *
+ * @param type
+ * @param callback
+ * @param context
+ * @returns {Viewer}
+ */
+ once(type, callback, context) {
+ this._viewerEvent.once(type, callback, context || this)
+ return this
+ }
+
+ /**
+ *
+ * @param type
+ * @param callback
+ * @param context
+ * @returns {Viewer}
+ */
+ off(type, callback, context) {
+ this._viewerEvent.off(type, callback, context || this)
+ this._sceneEvent.off(type, callback, context || this)
+ return this
+ }
+
+ /**
+ * Destroys the viewer.
+ */
+ destroy() {
+ Object.keys(this._layerCache).forEach((type) => {
+ let cache = this._layerCache[type]
+ Object.keys(cache).forEach((layerId) => {
+ this._removeLayer(cache[layerId])
+ })
+ })
+ this._delegate.destroy()
+ this._delegate = undefined
+ this._baseLayerPicker = undefined
+ this._layerCache = {}
+ this._dcContainer.parentNode.removeChild(this._dcContainer)
+ this._dcContainer = undefined
+ return this
+ }
+
+ /**
+ * Export scene to image
+ * @param name
+ * @returns {Viewer}
+ */
+ exportScene(name) {
+ this.scene.render()
+ let canvas = this.canvas
+ let image = canvas
+ .toDataURL('image/png')
+ .replace('image/png', 'image/octet-stream')
+ let link = document.createElement('a')
+ let blob = Util.dataURLtoBlob(image)
+ let objUrl = URL.createObjectURL(blob)
+ link.download = `${name || 'scene'}.png`
+ link.href = objUrl
+ link.click()
+ return this
+ }
+
+ /**
+ * Adds a plugin
+ * @param plugin
+ * @returns {Viewer}
+ */
+ use(plugin) {
+ if (plugin && plugin.install) {
+ plugin.install(this)
+ }
+ return this
+ }
+}
+
+export default Viewer
diff --git a/src/modules/weather/Weather.js b/src/modules/weather/Weather.js
new file mode 100644
index 00000000..50cf5e98
--- /dev/null
+++ b/src/modules/weather/Weather.js
@@ -0,0 +1,52 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-11-30 20:54:58
+ */
+
+import Fog from './type/Fog'
+import Rain from './type/Rain'
+import Snow from './type/Snow'
+import Cloud from './type/Cloud'
+
+class Weather {
+ constructor() {
+ this._comps = {
+ fog: new Fog(),
+ rain: new Rain(),
+ snow: new Snow(),
+ cloud: new Cloud(),
+ }
+ }
+
+ get fog() {
+ return this._comps.fog
+ }
+
+ get rain() {
+ return this._comps.rain
+ }
+
+ get snow() {
+ return this._comps.snow
+ }
+
+ get cloud() {
+ return this._comps.cloud
+ }
+
+ /**
+ *
+ * @param viewer
+ */
+ install(viewer) {
+ Object.keys(this._comps).forEach((key) => {
+ this._comps[key].addTo(viewer)
+ })
+ Object.defineProperty(viewer, 'weather', {
+ value: this,
+ writable: false,
+ })
+ }
+}
+
+export default Weather
diff --git a/src/modules/weather/type/Cloud.js b/src/modules/weather/type/Cloud.js
new file mode 100644
index 00000000..f95e8f98
--- /dev/null
+++ b/src/modules/weather/type/Cloud.js
@@ -0,0 +1,122 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-11-30 20:19:19
+ */
+
+import { Cesium } from '../../../namespace'
+import State from '../../state/State'
+import { Util } from '../../utils'
+import IMG from '../../images/cloud.jpg'
+
+class Cloud {
+ constructor() {
+ this._id = Util.uuid()
+ this._viewer = undefined
+ this._delegate = undefined
+ this._rotateAmount = 0
+ this._enable = false
+ this._heading = 0
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return 'cloud'
+ }
+
+ set enable(enable) {
+ if (!this._viewer.scene.mode === Cesium.SceneMode.SCENE3D) {
+ return
+ }
+ this._enable = this._delegate.show = enable
+ if (this._enable) {
+ this._viewer.scene.postUpdate.addEventListener(this._onRotate, this)
+ } else {
+ this._viewer.scene.postUpdate.removeEventListener(this._onRotate, this)
+ }
+ }
+
+ get enable() {
+ return this._enable
+ }
+
+ set rotateAmount(rotateAmount) {
+ this._rotateAmount = rotateAmount
+ }
+
+ get rotateAmount() {
+ return this._rotateAmount
+ }
+
+ /**
+ *
+ * @param scene
+ * @param time
+ * @private
+ */
+ _onRotate(scene, time) {
+ if (this._rotateAmount === 0) {
+ return
+ }
+ this._heading += this._rotateAmount
+ if (this._heading >= 360 || this._heading <= -360) {
+ this._heading = 0
+ }
+ this._delegate.modelMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(
+ new Cesium.Cartesian3(),
+ new Cesium.HeadingPitchRoll(Cesium.Math.toRadians(this._heading), 0, 0)
+ )
+ }
+
+ /**
+ *
+ * @private
+ */
+ _createPrimitive() {
+ this._delegate = new Cesium.Primitive({
+ appearance: new Cesium.EllipsoidSurfaceAppearance({
+ material: new Cesium.Material({
+ fabric: {
+ type: 'Image',
+ uniforms: {
+ color: new Cesium.Color(1.0, 1.0, 1.0, 1.0),
+ image: IMG,
+ },
+ components: {
+ alpha:
+ 'texture(image, fract(repeat * materialInput.st)).r * color.a',
+ diffuse: 'vec3(1.0)',
+ },
+ },
+ }),
+ translucent: true,
+ aboveGround: true,
+ }),
+ })
+ this._delegate.geometryInstances = new Cesium.GeometryInstance({
+ geometry: new Cesium.EllipsoidGeometry({
+ vertexFormat: Cesium.VertexFormat.POSITION_AND_ST,
+ radii: this._viewer.scene.globe.ellipsoid.radii,
+ }),
+ id: this._id,
+ })
+ this._delegate.show = this._enable
+ this._viewer.scene.primitives.add(this._delegate)
+ }
+
+ /**
+ *
+ * @param viewer
+ * @returns {Cloud}
+ */
+ addTo(viewer) {
+ if (!viewer) {
+ return this
+ }
+ this._viewer = viewer
+ this._createPrimitive()
+ this._state = State.ADDED
+ return this
+ }
+}
+
+export default Cloud
diff --git a/src/modules/weather/type/Fog.js b/src/modules/weather/type/Fog.js
new file mode 100644
index 00000000..2aefebd3
--- /dev/null
+++ b/src/modules/weather/type/Fog.js
@@ -0,0 +1,98 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-02-26 23:05:44
+ */
+
+import { Cesium } from '../../../namespace'
+import State from '../../state/State'
+import { Util } from '../../utils'
+import FogShader from '../../material/shader/weather/FogShader.glsl'
+
+class Fog {
+ constructor() {
+ this._id = Util.uuid()
+ this._viewer = undefined
+ this._delegate = undefined
+ this._enable = false
+ this._fogByDistance = { near: 10, nearValue: 0, far: 2000, farValue: 1.0 }
+ this._color = new Cesium.Color(0, 0, 0, 1)
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return 'fog'
+ }
+
+ set enable(enable) {
+ this._enable = enable
+ if (enable && this._viewer && !this._delegate) {
+ this._createPostProcessStage()
+ }
+ this._delegate && (this._delegate.enabled = enable)
+ }
+
+ get enable() {
+ return this._enable
+ }
+
+ set fogByDistance(fogByDistance) {
+ this._fogByDistance = fogByDistance
+ this._delegate &&
+ (this._delegate.uniforms.fogByDistance = new Cesium.Cartesian4(
+ this._fogByDistance?.near || 10,
+ this._fogByDistance?.nearValue || 0.0,
+ this._fogByDistance?.far || 2000,
+ this._fogByDistance?.farValue || 1.0
+ ))
+ }
+
+ get fogByDistance() {
+ return this._fogByDistance
+ }
+
+ set color(color) {
+ this._color = color
+ this._delegate && (this._delegate.uniforms.fogColor = color)
+ }
+
+ get color() {
+ return this._color
+ }
+
+ /**
+ *
+ * @private
+ */
+ _createPostProcessStage() {
+ this._delegate = new Cesium.PostProcessStage({
+ name: this._id,
+ fragmentShader: FogShader,
+ uniforms: {
+ fogByDistance: new Cesium.Cartesian4(
+ this._fogByDistance?.near || 10,
+ this._fogByDistance?.nearValue || 0.0,
+ this._fogByDistance?.far || 200,
+ this._fogByDistance?.farValue || 1.0
+ ),
+ fogColor: this._color,
+ },
+ })
+ this._viewer.scene.postProcessStages.add(this._delegate)
+ }
+
+ /**
+ *
+ * @param viewer
+ * @returns {Fog}
+ */
+ addTo(viewer) {
+ if (!viewer) {
+ return this
+ }
+ this._viewer = viewer
+ this._state = State.ADDED
+ return this
+ }
+}
+
+export default Fog
diff --git a/src/modules/weather/type/Rain.js b/src/modules/weather/type/Rain.js
new file mode 100644
index 00000000..bea7e309
--- /dev/null
+++ b/src/modules/weather/type/Rain.js
@@ -0,0 +1,88 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-15 20:23:42
+ */
+
+import { Cesium } from '../../../namespace'
+import State from '../../state/State'
+import { Util } from '../../utils'
+import RainShader from '../../material/shader/weather/RainShader.glsl'
+
+class Rain {
+ constructor() {
+ this._id = Util.uuid()
+ this._viewer = undefined
+ this._delegate = undefined
+ this._enable = false
+ this._speed = 10.0
+ this._mixNum = 0.5
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return 'rain'
+ }
+
+ set enable(enable) {
+ this._enable = enable
+ if (enable && this._viewer && !this._delegate) {
+ this._createPostProcessStage()
+ }
+ this._delegate && (this._delegate.enabled = enable)
+ return this
+ }
+
+ get enable() {
+ return this._enable
+ }
+
+ set speed(speed) {
+ this._speed = speed
+ this._delegate && (this._delegate.uniforms.speed = speed)
+ }
+
+ get speed() {
+ return this._speed
+ }
+
+ set mixNum(mixNum) {
+ this._mixNum = mixNum
+ this._delegate && (this._delegate.uniforms.mixNum = mixNum)
+ }
+
+ get mixNum() {
+ return this._mixNum
+ }
+
+ /**
+ *
+ * @private
+ */
+ _createPostProcessStage() {
+ this._delegate = new Cesium.PostProcessStage({
+ name: this._id,
+ fragmentShader: RainShader,
+ uniforms: {
+ speed: this._speed,
+ mixNum: this._mixNum,
+ },
+ })
+ this._viewer.scene.postProcessStages.add(this._delegate)
+ }
+
+ /**
+ *
+ * @param viewer
+ * @returns {Rain}
+ */
+ addTo(viewer) {
+ if (!viewer) {
+ return this
+ }
+ this._viewer = viewer
+ this._state = State.ADDED
+ return this
+ }
+}
+
+export default Rain
diff --git a/src/modules/weather/type/Snow.js b/src/modules/weather/type/Snow.js
new file mode 100644
index 00000000..8d3ccbd0
--- /dev/null
+++ b/src/modules/weather/type/Snow.js
@@ -0,0 +1,76 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-15 20:23:46
+ */
+
+import { Cesium } from '../../../namespace'
+import State from '../../state/State'
+import { Util } from '../../utils'
+import SnowShader from '../../material/shader/weather/SnowShader.glsl'
+
+class Snow {
+ constructor() {
+ this._id = Util.uuid()
+ this._viewer = undefined
+ this._delegate = undefined
+ this._enable = false
+ this._speed = 10.0
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return 'snow'
+ }
+
+ set enable(enable) {
+ this._enable = enable
+ if (enable && this._viewer && !this._delegate) {
+ this._createPostProcessStage()
+ }
+ this._delegate && (this._delegate.enabled = enable)
+ }
+
+ get enable() {
+ return this._enable
+ }
+
+ set speed(speed) {
+ this._speed = speed
+ this._delegate && (this._delegate.uniforms.speed = speed)
+ }
+
+ get speed() {
+ return this._speed
+ }
+
+ /**
+ *
+ * @private
+ */
+ _createPostProcessStage() {
+ this._delegate = new Cesium.PostProcessStage({
+ name: this._id,
+ fragmentShader: SnowShader,
+ uniforms: {
+ speed: this._speed,
+ },
+ })
+ this._viewer.scene.postProcessStages.add(this._delegate)
+ }
+
+ /**
+ *
+ * @param viewer
+ * @returns {Snow}
+ */
+ addTo(viewer) {
+ if (!viewer) {
+ return this
+ }
+ this._viewer = viewer
+ this._state = State.ADDED
+ return this
+ }
+}
+
+export default Snow
diff --git a/src/modules/widget/Widget.js b/src/modules/widget/Widget.js
new file mode 100644
index 00000000..20c00cf1
--- /dev/null
+++ b/src/modules/widget/Widget.js
@@ -0,0 +1,150 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-15 19:17:52
+ */
+
+import State from '../state/State'
+import WidgetType from './WidgetType'
+
+class Widget {
+ constructor() {
+ this._viewer = undefined
+ this._enable = false
+ this._wrapper = undefined
+ this._ready = false
+ }
+
+ set enable(enable) {
+ if (this._enable === enable) {
+ return
+ }
+ this._enable = enable
+ this._state = this._enable ? State.ENABLED : State.DISABLED
+ this._enableHook && this._enableHook()
+ }
+
+ get enable() {
+ return this._enable
+ }
+
+ get state() {
+ return this._state
+ }
+
+ /**
+ * mount content
+ * @private
+ */
+ _mountContent() {}
+
+ /**
+ * binds event
+ * @private
+ */
+ _bindEvent() {}
+
+ /**
+ * Unbinds event
+ * @private
+ */
+ _unbindEvent() {}
+
+ /**
+ * When enable modifies the hook executed, the subclass copies it as required
+ * @private
+ */
+ _enableHook() {
+ !this._ready && this._mountContent()
+ if (this._enable) {
+ !this._wrapper.parentNode &&
+ this._viewer.dcContainer.appendChild(this._wrapper)
+ this._bindEvent()
+ } else {
+ this._unbindEvent()
+ this._wrapper.parentNode &&
+ this._viewer.dcContainer.removeChild(this._wrapper)
+ }
+ }
+
+ /**
+ * Updating the Widget location requires subclass overrides
+ * @param windowCoord
+ * @private
+ */
+ _updateWindowCoord(windowCoord) {}
+
+ /**
+ * Hook for installed
+ * @private
+ */
+ _installHook() {}
+
+ /**
+ * Installs to viewer
+ * @param viewer
+ */
+ install(viewer) {
+ this._viewer = viewer
+ /**
+ * do installHook
+ */
+ this._installHook && this._installHook()
+ this._state = State.INSTALLED
+ }
+
+ /**
+ * Setting wrapper
+ * @param wrapper
+ * @returns {Widget}
+ */
+ setWrapper(wrapper) {
+ return this
+ }
+
+ /**
+ * Setting widget content
+ * @param content
+ * @returns {Widget}
+ */
+ setContent(content) {
+ if (content && typeof content === 'string') {
+ this._wrapper.innerHTML = content
+ } else if (content && content instanceof Element) {
+ while (this._wrapper.hasChildNodes()) {
+ this._wrapper.removeChild(this._wrapper.firstChild)
+ }
+ this._wrapper.appendChild(content)
+ }
+ return this
+ }
+
+ /**
+ * hide widget
+ */
+ hide() {
+ this._wrapper &&
+ (this._wrapper.style.cssText = `
+ visibility:hidden;
+ `)
+ }
+
+ /**
+ * Registers type
+ * @param type
+ */
+ static registerType(type) {
+ if (type) {
+ WidgetType[type.toLocaleUpperCase()] = type.toLocaleLowerCase()
+ }
+ }
+
+ /**
+ *
+ * @param type
+ */
+ static getWidgetType(type) {
+ return WidgetType[type.toLocaleUpperCase()] || undefined
+ }
+}
+
+export default Widget
diff --git a/src/modules/widget/WidgetType.js b/src/modules/widget/WidgetType.js
new file mode 100644
index 00000000..26174a9e
--- /dev/null
+++ b/src/modules/widget/WidgetType.js
@@ -0,0 +1,8 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-05-10 10:15:46
+ */
+
+let WidgetType = {}
+
+export default WidgetType
diff --git a/src/modules/widget/index.js b/src/modules/widget/index.js
new file mode 100644
index 00000000..99ef7f78
--- /dev/null
+++ b/src/modules/widget/index.js
@@ -0,0 +1,36 @@
+/**
+ * @Author:Caven
+ * @Date:2020-03-05 21:53:35
+ */
+
+import ContextMenu from './type/ContextMenu'
+import LocationBar from './type/LocationBar'
+import MapSplit from './type/MapSplit'
+import MapSwitch from './type/MapSwitch'
+import Popup from './type/Popup'
+import Tooltip from './type/Tooltip'
+import HawkeyeMap from './type/HawkeyeMap'
+import Compass from './type/Compass'
+import DistanceLegend from './type/DistanceLegend'
+import ZoomController from './type/ZoomController'
+import LoadingMask from './type/LoadingMask'
+import TilesetSplit from './type/TilesetSplit'
+import SceneSplit from './type/SceneSplit'
+
+export default function createWidgets() {
+ return {
+ popup: new Popup(),
+ contextMenu: new ContextMenu(),
+ tooltip: new Tooltip(),
+ mapSwitch: new MapSwitch(),
+ mapSplit: new MapSplit(),
+ locationBar: new LocationBar(),
+ hawkeyeMap: new HawkeyeMap(),
+ compass: new Compass(),
+ distanceLegend: new DistanceLegend(),
+ zoomController: new ZoomController(),
+ loadingMask: new LoadingMask(),
+ tilesetSplit: new TilesetSplit(),
+ sceneSplit: new SceneSplit()
+ }
+}
diff --git a/src/modules/widget/type/Compass.js b/src/modules/widget/type/Compass.js
new file mode 100644
index 00000000..c16e06e1
--- /dev/null
+++ b/src/modules/widget/type/Compass.js
@@ -0,0 +1,454 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-03-19 13:11:12
+ */
+
+import { Cesium } from '../../../namespace'
+import State from '../../state/State'
+import Icons from '../../icons'
+import { DomUtil } from '../../utils'
+import { SceneEventType } from '../../event'
+import Widget from '../Widget'
+
+class Compass extends Widget {
+ constructor() {
+ super()
+ this._wrapper = DomUtil.create('div', `dc-compass`)
+ this._compassRectangle = undefined
+ this._outRing = undefined
+ this._gyro = undefined
+ this._rotation_marker = undefined
+ this._orbitCursorAngle = 0
+ this._orbitCursorOpacity = 0.0
+ this._orbitLastTimestamp = 0
+ this._orbitFrame = undefined
+ this._orbitIsLook = false
+ this._rotateInitialCursorAngle = undefined
+ this._rotateFrame = undefined
+ this._mouseMoveHandle = undefined
+ this._mouseUpHandle = undefined
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Widget.getWidgetType('compass')
+ }
+
+ /**
+ *
+ * @private
+ */
+ _installHook() {
+ Object.defineProperty(this._viewer, 'compass', {
+ value: this,
+ writable: false,
+ })
+ this._wrapper.onmousedown = (e) => {
+ this._handleMouseDown(e)
+ }
+ this._wrapper.ondblclick = (e) => {
+ this._handleDoubleClick(e)
+ }
+ }
+
+ /**
+ *
+ * @private
+ */
+ _bindEvent() {
+ this._viewer.on(SceneEventType.POST_RENDER, this._postRenderHandler, this)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _unbindEvent() {
+ this._viewer.off(SceneEventType.POST_RENDER, this._postRenderHandler, this)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _postRenderHandler() {
+ let heading = this._viewer.camera.heading
+ this._outRing &&
+ (this._outRing.style.cssText = `
+ transform : rotate(-${heading}rad);
+ -webkit-transform : rotate(-${heading}rad);
+ `)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountContent() {
+ DomUtil.create('div', 'out-ring-bg', this._wrapper)
+ this._outRing = DomUtil.parseDom(Icons.compass_outer, true, 'out-ring')
+ this._wrapper.appendChild(this._outRing)
+ this._gyro = DomUtil.parseDom(Icons.compass_inner, true, 'gyro')
+ this._wrapper.appendChild(this._gyro)
+ this._rotation_marker = DomUtil.parseDom(
+ Icons.compass_rotation_marker,
+ true,
+ 'rotation_marker'
+ )
+ this._wrapper.appendChild(this._rotation_marker)
+ this._rotation_marker.style.visibility = 'hidden'
+ this._ready = true
+ }
+
+ _handleMouseDown(e) {
+ let scene = this._viewer.scene
+ if (scene.mode === Cesium.SceneMode.MORPHING) {
+ return true
+ }
+ this._compassRectangle = e.currentTarget.getBoundingClientRect()
+ let maxDistance = this._compassRectangle.width / 2.0
+ let vector = this._getVector(e)
+ let distanceFraction = Cesium.Cartesian2.magnitude(vector) / maxDistance
+ if (distanceFraction < 50 / 145) {
+ this._orbit(vector)
+ } else if (distanceFraction < 1.0) {
+ this._rotate(vector)
+ } else {
+ return true
+ }
+ }
+
+ _handleDoubleClick() {
+ let scene = this._viewer.scene
+ let camera = scene.camera
+ let sscc = scene.screenSpaceCameraController
+ if (scene.mode === Cesium.SceneMode.MORPHING || !sscc.enableInputs) {
+ return true
+ }
+ if (
+ scene.mode === Cesium.SceneMode.COLUMBUS_VIEW &&
+ !sscc.enableTranslate
+ ) {
+ return
+ }
+ if (
+ scene.mode === Cesium.SceneMode.SCENE3D ||
+ scene.mode === Cesium.SceneMode.COLUMBUS_VIEW
+ ) {
+ if (!sscc.enableLook) {
+ return
+ }
+ if (scene.mode === Cesium.SceneMode.SCENE3D) {
+ if (!sscc.enableRotate) {
+ return
+ }
+ }
+ }
+ let center = this._getCameraFocus(true)
+ if (!center) {
+ return
+ }
+ let cameraPosition = scene.globe.ellipsoid.cartographicToCartesian(
+ camera.positionCartographic
+ )
+ let surfaceNormal = scene.globe.ellipsoid.geodeticSurfaceNormal(center)
+ let focusBoundingSphere = new Cesium.BoundingSphere(center, 0)
+ camera.flyToBoundingSphere(focusBoundingSphere, {
+ offset: new Cesium.HeadingPitchRange(
+ 0,
+ Cesium.Math.PI_OVER_TWO -
+ Cesium.Cartesian3.angleBetween(surfaceNormal, camera.directionWC),
+ Cesium.Cartesian3.distance(cameraPosition, center)
+ ),
+ duration: 1.5,
+ })
+ }
+
+ _getCameraFocus(inWorldCoordinates) {
+ let result = new Cesium.Cartesian3()
+ let scene = this._viewer.scene
+ let camera = scene.camera
+ if (scene.mode === Cesium.SceneMode.MORPHING) {
+ return undefined
+ }
+ if (this._viewer.delegate.trackedEntity) {
+ result = this._viewer.delegate.trackedEntity.position.getValue(
+ this._viewer.clock.currentTime
+ )
+ } else {
+ let rayScratch = new Cesium.Ray()
+ rayScratch.origin = camera.positionWC
+ rayScratch.direction = camera.directionWC
+ result = scene.globe.pick(rayScratch, scene)
+ }
+ if (!result) {
+ return undefined
+ }
+ if (
+ scene.mode === Cesium.SceneMode.SCENE2D ||
+ scene.mode === Cesium.SceneMode.COLUMBUS_VIEW
+ ) {
+ result = camera.worldToCameraCoordinatesPoint(result)
+ let unprojectedScratch = new Cesium.Cartographic()
+ if (inWorldCoordinates) {
+ result = scene.globe.ellipsoid.cartographicToCartesian(
+ scene.mapProjection.unproject(result, unprojectedScratch)
+ )
+ }
+ } else {
+ if (!inWorldCoordinates) {
+ result = camera.worldToCameraCoordinatesPoint(result)
+ }
+ }
+ return result
+ }
+
+ _orbit(vector) {
+ let scene = this._viewer.scene
+ let sscc = scene.screenSpaceCameraController
+ let camera = scene.camera
+ if (scene.mode === Cesium.SceneMode.MORPHING || !sscc.enableInputs) {
+ return
+ }
+ switch (scene.mode) {
+ case Cesium.SceneMode.COLUMBUS_VIEW:
+ if (sscc.enableLook) {
+ break
+ }
+ if (!sscc.enableTranslate || !sscc.enableTilt) {
+ return
+ }
+ break
+ case Cesium.SceneMode.SCENE3D:
+ if (sscc.enableLook) {
+ break
+ }
+ if (!sscc.enableTilt || !sscc.enableRotate) {
+ return
+ }
+ break
+ case Cesium.SceneMode.SCENE2D:
+ if (!sscc.enableTranslate) {
+ return
+ }
+ break
+ }
+
+ this._mouseMoveHandle = (e) => {
+ this._orbitMouseMoveFunction(e)
+ }
+ this._mouseUpHandle = () => {
+ this._orbitMouseUpFunction()
+ }
+
+ document.removeEventListener('mousemove', this._mouseMoveHandle, false)
+ document.removeEventListener('mouseup', this._mouseUpHandle, false)
+
+ this._orbitLastTimestamp = Cesium.getTimestamp()
+
+ if (this._viewer.delegate.trackedEntity) {
+ this._orbitFrame = undefined
+ this._orbitIsLook = false
+ } else {
+ let center = this._getCameraFocus(true)
+
+ if (!center) {
+ this._orbitFrame = Cesium.Transforms.eastNorthUpToFixedFrame(
+ camera.positionWC,
+ scene.globe.ellipsoid
+ )
+ this._orbitIsLook = true
+ } else {
+ this._orbitFrame = Cesium.Transforms.eastNorthUpToFixedFrame(
+ center,
+ scene.globe.ellipsoid
+ )
+ this._orbitIsLook = false
+ }
+ }
+
+ this._rotation_marker.style.visibility = 'visible'
+ this._gyro.className += ' gyro-active'
+ document.addEventListener('mousemove', this._mouseMoveHandle, false)
+ document.addEventListener('mouseup', this._mouseUpHandle, false)
+ this._viewer.clock.onTick.addEventListener(this._orbitTickFunction, this)
+ this._updateAngleAndOpacity(vector, this._compassRectangle.width)
+ }
+
+ _orbitTickFunction() {
+ let scene = this._viewer.scene
+ let camera = this._viewer.camera
+ let timestamp = Cesium.getTimestamp()
+ let deltaT = timestamp - this._orbitLastTimestamp
+ let rate = ((this._orbitCursorOpacity - 0.5) * 2.5) / 1000
+ let distance = deltaT * rate
+ let angle = this._orbitCursorAngle + Cesium.Math.PI_OVER_TWO
+ let x = Math.cos(angle) * distance
+ let y = Math.sin(angle) * distance
+ let oldTransform
+
+ if (this._orbitFrame) {
+ oldTransform = Cesium.Matrix4.clone(camera.transform)
+ camera.lookAtTransform(this._orbitFrame)
+ }
+
+ if (scene.mode === Cesium.SceneMode.SCENE2D) {
+ camera.move(
+ new Cesium.Cartesian3(x, y, 0),
+ (Math.max(scene.canvas.clientWidth, scene.canvas.clientHeight) / 100) *
+ camera.positionCartographic.height *
+ distance
+ )
+ } else {
+ if (this._orbitIsLook) {
+ camera.look(Cesium.Cartesian3.UNIT_Z, -x)
+ camera.look(camera.right, -y)
+ } else {
+ camera.rotateLeft(x)
+ camera.rotateUp(y)
+ }
+ }
+ if (this._orbitFrame && oldTransform) {
+ camera.lookAtTransform(oldTransform)
+ }
+ this._orbitLastTimestamp = timestamp
+ }
+
+ _updateAngleAndOpacity(vector, compassWidth) {
+ let angle = Math.atan2(-vector.y, vector.x)
+ this._orbitCursorAngle = Cesium.Math.zeroToTwoPi(
+ angle - Cesium.Math.PI_OVER_TWO
+ )
+ let distance = Cesium.Cartesian2.magnitude(vector)
+ let maxDistance = compassWidth / 2.0
+ let distanceFraction = Math.min(distance / maxDistance, 1.0)
+ this._orbitCursorOpacity = 0.5 * distanceFraction * distanceFraction + 0.5
+ this._rotation_marker.style.cssText = `
+ transform: rotate(-${this._orbitCursorAngle}rad);
+ opacity: ${this._orbitCursorOpacity}`
+ }
+
+ _orbitMouseMoveFunction(e) {
+ this._updateAngleAndOpacity(
+ this._getVector(e),
+ this._compassRectangle.width
+ )
+ }
+
+ _orbitMouseUpFunction() {
+ document.removeEventListener('mousemove', this._mouseMoveHandle, false)
+ document.removeEventListener('mouseup', this._mouseUpHandle, false)
+ this._viewer.clock.onTick.removeEventListener(this._orbitTickFunction, this)
+ this._mouseMoveHandle = undefined
+ this._mouseUpHandle = undefined
+ this._rotation_marker.style.visibility = 'hidden'
+ this._gyro.className = this._gyro.className.replace(' gyro-active', '')
+ }
+
+ _rotate(vector) {
+ let scene = this._viewer.scene
+ let camera = scene.camera
+ let sscc = scene.screenSpaceCameraController
+ if (
+ scene.mode === Cesium.SceneMode.MORPHING ||
+ scene.mode === Cesium.SceneMode.SCENE2D ||
+ !sscc.enableInputs
+ ) {
+ return
+ }
+ if (
+ !sscc.enableLook &&
+ (scene.mode === Cesium.SceneMode.COLUMBUS_VIEW ||
+ (scene.mode === Cesium.SceneMode.SCENE3D && !sscc.enableRotate))
+ ) {
+ return
+ }
+ this._mouseMoveHandle = (e) => {
+ this._rotateMouseMoveFunction(e)
+ }
+ this._mouseUpHandle = () => {
+ this._rotateMouseUpFunction()
+ }
+ document.removeEventListener('mousemove', this._mouseMoveHandle, false)
+ document.removeEventListener('mouseup', this._mouseUpHandle, false)
+ this._rotateInitialCursorAngle = Math.atan2(-vector.y, vector.x)
+ if (this._viewer.delegate.trackedEntity) {
+ this._rotateFrame = undefined
+ } else {
+ let center = this._getCameraFocus(true)
+ if (
+ !center ||
+ (scene.mode === Cesium.SceneMode.COLUMBUS_VIEW &&
+ !sscc.enableLook &&
+ !sscc.enableTranslate)
+ ) {
+ this._rotateFrame = Cesium.Transforms.eastNorthUpToFixedFrame(
+ camera.positionWC,
+ scene.globe.ellipsoid
+ )
+ } else {
+ this._rotateFrame = Cesium.Transforms.eastNorthUpToFixedFrame(
+ center,
+ scene.globe.ellipsoid
+ )
+ }
+ }
+ let oldTransform
+ if (this._rotateFrame) {
+ oldTransform = Cesium.Matrix4.clone(camera.transform)
+ camera.lookAtTransform(this._rotateFrame)
+ }
+ this._rotateInitialCameraAngle = -camera.heading
+ if (this._rotateFrame && oldTransform) {
+ camera.lookAtTransform(oldTransform)
+ }
+ document.addEventListener('mousemove', this._mouseMoveHandle, false)
+ document.addEventListener('mouseup', this._mouseUpHandle, false)
+ }
+
+ _rotateMouseMoveFunction(e) {
+ let camera = this._viewer.camera
+ let vector = this._getVector(e)
+ let angle = Math.atan2(-vector.y, vector.x)
+ let angleDifference = angle - this._rotateInitialCursorAngle
+ let newCameraAngle = Cesium.Math.zeroToTwoPi(
+ this._rotateInitialCameraAngle - angleDifference
+ )
+ let oldTransform
+ if (this._rotateFrame) {
+ oldTransform = Cesium.Matrix4.clone(camera.transform)
+ camera.lookAtTransform(this._rotateFrame)
+ }
+ let currentCameraAngle = -camera.heading
+ camera.rotateRight(newCameraAngle - currentCameraAngle)
+ if (this._rotateFrame && oldTransform) {
+ camera.lookAtTransform(oldTransform)
+ }
+ }
+
+ _rotateMouseUpFunction() {
+ document.removeEventListener('mousemove', this._mouseMoveHandle, false)
+ document.removeEventListener('mouseup', this._mouseUpHandle, false)
+ this._mouseMoveHandle = undefined
+ this._mouseUpHandle = undefined
+ }
+
+ _getVector(e) {
+ let compassRectangle = this._compassRectangle
+ let center = new Cesium.Cartesian2(
+ (compassRectangle.right - compassRectangle.left) / 2.0,
+ (compassRectangle.bottom - compassRectangle.top) / 2.0
+ )
+ let clickLocation = new Cesium.Cartesian2(
+ e.clientX - compassRectangle.left,
+ e.clientY - compassRectangle.top
+ )
+ let vector = new Cesium.Cartesian2()
+ Cesium.Cartesian2.subtract(clickLocation, center, vector)
+ return vector
+ }
+}
+
+Widget.registerType('compass')
+
+export default Compass
diff --git a/src/modules/widget/type/ContextMenu.js b/src/modules/widget/type/ContextMenu.js
new file mode 100644
index 00000000..2c769f77
--- /dev/null
+++ b/src/modules/widget/type/ContextMenu.js
@@ -0,0 +1,294 @@
+/**
+ * @Author: Caven
+ * @Date: 2019-12-31 17:32:01
+ */
+
+import { Cesium } from '../../../namespace'
+import State from '../../state/State'
+import { DomUtil } from '../../utils'
+import Widget from '../Widget'
+
+class ContextMenu extends Widget {
+ constructor() {
+ super()
+ this._wrapper = DomUtil.create('div', 'dc-context-menu')
+ this._ulEl = undefined
+ this._handler = undefined
+ this._overlay = undefined
+ this._position = undefined
+ this._wgs84Position = undefined
+ this._surfacePosition = undefined
+ this._wgs84SurfacePosition = undefined
+ this._windowPosition = undefined
+ this._instanceId = undefined
+ this._config = {}
+ this._defaultMenu = [
+ {
+ label: '飞到默认位置',
+ callback: () => {
+ this._viewer.camera.flyHome(1.5)
+ },
+ context: this,
+ },
+ {
+ label: '取消飞行',
+ callback: () => {
+ this._viewer.camera.cancelFlight()
+ },
+ context: this,
+ },
+ ]
+ this._overlayMenu = []
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Widget.getWidgetType('contextmenu')
+ }
+
+ set DEFAULT_MENU(menus) {
+ this._defaultMenu = menus
+ }
+
+ set config(config) {
+ this._config = config
+ config.customClass && this._setCustomClass()
+ }
+
+ /**
+ *
+ * @private
+ */
+ _installHook() {
+ Object.defineProperty(this._viewer, 'contextMenu', {
+ value: this,
+ writable: false,
+ })
+ this._handler = new Cesium.ScreenSpaceEventHandler(this._viewer.canvas)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _bindEvent() {
+ this._handler.setInputAction((movement) => {
+ this._onRightClick(movement)
+ }, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
+
+ this._handler.setInputAction((movement) => {
+ this._onClick(movement)
+ }, Cesium.ScreenSpaceEventType.LEFT_CLICK)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _unbindEvent() {
+ this._handler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK)
+ this._handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountContent() {
+ this._ulEl = DomUtil.create('ul', 'menu-list', this._wrapper)
+ this._ready = true
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountMenu() {
+ while (this._ulEl.hasChildNodes()) {
+ this._ulEl.removeChild(this._ulEl.firstChild)
+ }
+ // Add menu item
+ if (this._overlayMenu && this._overlayMenu.length) {
+ this._overlayMenu.forEach((item) => {
+ this._addMenuItem(item.label, item.callback, item.context || this)
+ })
+ }
+
+ if (this._defaultMenu && this._defaultMenu.length) {
+ this._defaultMenu.forEach((item) => {
+ this._addMenuItem(item.label, item.callback, item.context || this)
+ })
+ }
+ }
+
+ /**
+ *
+ * @param movement
+ * @private
+ */
+ _onRightClick(movement) {
+ if (!this._enable) {
+ return
+ }
+ this._overlay = undefined
+ let scene = this._viewer.scene
+ this._windowPosition = movement.position
+ let target = scene.pick(movement.position)
+ if (scene.pickPositionSupported) {
+ this._position = scene.pickPosition(movement.position)
+ }
+
+ if (this._position) {
+ let c = Cesium.Ellipsoid.WGS84.cartesianToCartographic(this._position)
+ if (c) {
+ this._wgs84Position = {
+ lng: Cesium.Math.toDegrees(c.longitude),
+ lat: Cesium.Math.toDegrees(c.latitude),
+ alt: c.height,
+ }
+ }
+ }
+
+ if (scene.mode === Cesium.SceneMode.SCENE3D) {
+ let ray = scene.camera.getPickRay(movement.position)
+ this._surfacePosition = scene.globe.pick(ray, scene)
+ } else {
+ this._surfacePosition = scene.camera.pickEllipsoid(
+ movement.position,
+ Cesium.Ellipsoid.WGS84
+ )
+ }
+
+ if (this._surfacePosition) {
+ let c = Cesium.Ellipsoid.WGS84.cartesianToCartographic(
+ this._surfacePosition
+ )
+ if (c) {
+ this._wgs84SurfacePosition = {
+ lng: Cesium.Math.toDegrees(c.longitude),
+ lat: Cesium.Math.toDegrees(c.latitude),
+ alt: c.height,
+ }
+ }
+ }
+
+ this._instanceId = target?.instanceId
+ // for Entity
+ if (target?.id instanceof Cesium.Entity) {
+ let layer = this._viewer
+ .getLayers()
+ .filter((item) => item.layerId === target.id.layerId)[0]
+ if (layer && layer.getOverlay) {
+ this._overlay = layer.getOverlay(target.id.overlayId)
+ }
+ }
+
+ // for Cesium3DTileFeature
+ else if (target instanceof Cesium.Cesium3DTileFeature) {
+ let layer = this._viewer
+ .getLayers()
+ .filter((item) => item.layerId === target.tileset.layerId)[0]
+ if (layer && layer.getOverlay) {
+ this._overlay = layer.getOverlay(target.tileset.overlayId)
+ }
+ }
+
+ // for Cesium3DTileset
+ else if (target?.primitive instanceof Cesium.Cesium3DTileset) {
+ let layer = this._viewer
+ .getLayers()
+ .filter((item) => item.layerId === target.primitive.layerId)[0]
+ if (layer && layer.getOverlay) {
+ this._overlay = layer.getOverlay(target.primitive.overlayId)
+ }
+ }
+
+ // for Primitive
+ else if (target?.primitive) {
+ let layer = this._viewer
+ .getLayers()
+ .filter((item) => item.layerId === target.primitive.layerId)[0]
+ if (layer && layer.getOverlay) {
+ this._overlay = layer.getOverlay(target.primitive.overlayId)
+ }
+ }
+
+ this._overlayMenu = this._overlay?.contextMenu || []
+ this._mountMenu()
+ this._updateWindowCoord(movement.position)
+ }
+ /**
+ *
+ * @param movement
+ * @private
+ */
+ _onClick(movement) {
+ this.hide()
+ }
+
+ /**
+ *
+ * @param windowCoord
+ * @private
+ */
+ _updateWindowCoord(windowCoord) {
+ let visibility = this._ulEl.hasChildNodes() ? 'visible' : 'hidden'
+ this._wrapper.style.cssText = `
+ visibility:${visibility};
+ z-index:1;
+ transform:translate3d(${Math.round(windowCoord.x)}px,${Math.round(
+ windowCoord.y
+ )}px, 0);
+ `
+ }
+
+ /**
+ *
+ * @private
+ */
+ _setCustomClass() {
+ DomUtil.setClass(
+ this._wrapper,
+ `dc-context-menu ${this._config.customClass}`
+ )
+ }
+
+ /**
+ *
+ * @param label
+ * @param method
+ * @param context
+ * @returns {ContextMenu}
+ * @private
+ */
+ _addMenuItem(label, method, context) {
+ if (!label || !method) {
+ return this
+ }
+ let menu = DomUtil.create('li', 'menu-item', null)
+ let a = DomUtil.create('a', '', menu)
+ a.innerHTML = label
+ a.href = 'javascript:void(0)'
+ let self = this
+ if (method) {
+ a.onclick = () => {
+ method.call(context, {
+ windowPosition: self._windowPosition,
+ position: self._position,
+ wgs84Position: self._wgs84Position,
+ surfacePosition: self._surfacePosition,
+ wgs84SurfacePosition: self._wgs84SurfacePosition,
+ overlay: self._overlay,
+ instanceId: self._instanceId,
+ })
+ self.hide()
+ }
+ }
+ this._ulEl.appendChild(menu)
+ return this
+ }
+}
+
+Widget.registerType('contextmenu')
+
+export default ContextMenu
diff --git a/src/modules/widget/type/DistanceLegend.js b/src/modules/widget/type/DistanceLegend.js
new file mode 100644
index 00000000..cad0262e
--- /dev/null
+++ b/src/modules/widget/type/DistanceLegend.js
@@ -0,0 +1,134 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-12-09 20:54:06
+ */
+
+import { Cesium } from '../../../namespace'
+import State from '../../state/State'
+import { DomUtil } from '../../utils'
+import { SceneEventType } from '../../event'
+import Widget from '../Widget'
+
+const geodesic = new Cesium.EllipsoidGeodesic()
+
+const BASE = [1, 2, 3, 5]
+
+const DIS = [
+ ...BASE,
+ ...BASE.map((item) => item * 10),
+ ...BASE.map((item) => item * 100),
+ ...BASE.map((item) => item * 1000),
+ ...BASE.map((item) => item * 10000),
+ ...BASE.map((item) => item * 100000),
+ ...BASE.map((item) => item * 1000000),
+]
+
+class DistanceLegend extends Widget {
+ constructor() {
+ super()
+ this._wrapper = DomUtil.create('div', 'dc-distance-legend')
+ this._labelEl = undefined
+ this._scaleBarEl = undefined
+ this._lastUpdate = Cesium.getTimestamp()
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Widget.getWidgetType('distance_legend')
+ }
+
+ /**
+ *
+ * @private
+ */
+ _installHook() {
+ Object.defineProperty(this._viewer, 'distanceLegend', {
+ value: this,
+ writable: false,
+ })
+ }
+
+ /**
+ *
+ * @private
+ */
+ _bindEvent() {
+ this._viewer.on(SceneEventType.POST_RENDER, this._updateContent, this)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _unbindEvent() {
+ this._viewer.off(SceneEventType.POST_RENDER, this._updateContent, this)
+ }
+
+ /**
+ *
+ * @param scene
+ * @param time
+ * @returns
+ * @private
+ */
+ _updateContent(scene, time) {
+ let now = Cesium.getTimestamp()
+ if (now < this._lastUpdate + 250) {
+ return
+ }
+ if (!this._labelEl || !this._scaleBarEl) {
+ return
+ }
+ this._lastUpdate = now
+ let width = scene.canvas.width
+ let height = scene.canvas.height
+ let left = scene.camera.getPickRay(
+ new Cesium.Cartesian2((width / 2) | 0, height - 1)
+ )
+ let right = scene.camera.getPickRay(
+ new Cesium.Cartesian2((1 + width / 2) | 0, height - 1)
+ )
+ let leftPosition = scene.globe.pick(left, scene)
+ let rightPosition = scene.globe.pick(right, scene)
+ if (!leftPosition || !rightPosition) {
+ return
+ }
+ geodesic.setEndPoints(
+ scene.globe.ellipsoid.cartesianToCartographic(leftPosition),
+ scene.globe.ellipsoid.cartesianToCartographic(rightPosition)
+ )
+ let pixelDistance = geodesic.surfaceDistance
+ let maxBarWidth = 100
+ let distance = 0
+ for (let i = DIS.length - 1; i >= 0; --i) {
+ if (DIS[i] / pixelDistance < maxBarWidth) {
+ distance = DIS[i]
+ break
+ }
+ }
+ if (distance) {
+ this._wrapper.style.visibility = 'visible'
+ this._labelEl.innerHTML =
+ distance >= 1000 ? `${distance / 1000} km` : `${distance} m`
+ let barWidth = (distance / pixelDistance) | 0
+ this._scaleBarEl.style.cssText = `width: ${barWidth}px; left: ${
+ (125 - barWidth) / 2
+ }px;`
+ }
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountContent() {
+ this._labelEl = DomUtil.create('div', 'label', this._wrapper)
+ this._scaleBarEl = DomUtil.create('div', 'scale-bar', this._wrapper)
+ this._wrapper.style.visibility = 'hidden'
+ this._ready = true
+ }
+}
+
+Widget.registerType('distance_legend')
+
+export default DistanceLegend
diff --git a/src/modules/widget/type/HawkeyeMap.js b/src/modules/widget/type/HawkeyeMap.js
new file mode 100644
index 00000000..52c8d4d4
--- /dev/null
+++ b/src/modules/widget/type/HawkeyeMap.js
@@ -0,0 +1,152 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-03-15 17:47:42
+ */
+
+import { Cesium } from '../../../namespace'
+import State from '../../state/State'
+import { Util, DomUtil } from '../../utils'
+import { SceneEventType } from '../../event'
+import Widget from '../Widget'
+
+const DEF_OPTS = {
+ animation: false,
+ baseLayerPicker: false,
+ imageryProvider: false,
+ fullscreenButton: false,
+ geocoder: false,
+ homeButton: false,
+ infoBox: false,
+ sceneModePicker: false,
+ selectionIndicator: false,
+ timeline: false,
+ navigationHelpButton: false,
+ navigationInstructionsInitiallyVisible: false,
+ creditContainer: undefined,
+}
+
+class HawkeyeMap extends Widget {
+ constructor() {
+ super()
+ this._wrapper = DomUtil.create('div', 'dc-hawkeye-map', null)
+ this._wrapper.setAttribute('id', Util.uuid())
+ this._baseLayers = []
+ this._map = undefined
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Widget.getWidgetType('hawkeye_map')
+ }
+
+ get baseLayers() {
+ return this._baseLayers
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountContent() {
+ let map = new Cesium.Viewer(this._wrapper, {
+ ...DEF_OPTS,
+ sceneMode: Cesium.SceneMode.SCENE2D,
+ })
+ map.imageryLayers.removeAll()
+ map.cesiumWidget.creditContainer.style.display = 'none'
+ map.cesiumWidget.screenSpaceEventHandler.removeInputAction(
+ Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK
+ )
+ map.scene.backgroundColor = Cesium.Color.TRANSPARENT
+ Util.merge(map.scene.screenSpaceCameraController, {
+ enableRotate: false,
+ enableTranslate: false,
+ enableZoom: false,
+ enableTilt: false,
+ enableLook: false,
+ maximumZoomDistance: 40489014.0,
+ })
+ this._map = map
+
+ this._ready = true
+ }
+
+ /**
+ *
+ * @private
+ */
+ _bindEvent() {
+ this._viewer.on(SceneEventType.CAMERA_CHANGED, this._syncMap, this)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _unbindEvent() {
+ this._viewer.off(SceneEventType.CAMERA_CHANGED, this._syncMap, this)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _installHook() {
+ Object.defineProperty(this._viewer, 'hawkeyeMap', {
+ value: this,
+ writable: false,
+ })
+ this._viewer.camera.percentageChanged = 0.01
+ }
+
+ /**
+ *
+ * @returns {boolean}
+ * @private
+ */
+ _syncMap() {
+ let viewCenter = new Cesium.Cartesian2(
+ Math.floor(this._viewer.canvas.clientWidth / 2),
+ Math.floor(this._viewer.canvas.clientHeight / 2)
+ )
+ let worldPosition = this._viewer.scene.camera.pickEllipsoid(viewCenter)
+ if (!worldPosition) {
+ return false
+ }
+ let distance = Cesium.Cartesian3.distance(
+ worldPosition,
+ this._viewer.scene.camera.positionWC
+ )
+ this._map.scene.camera.lookAt(
+ worldPosition,
+ new Cesium.Cartesian3(0.0, 0.0, distance)
+ )
+ }
+
+ /**
+ *
+ * @param baseLayer
+ * @returns {HawkeyeMap}
+ */
+ addBaseLayer(baseLayer) {
+ if (!this._map || !this._enable) {
+ return this
+ }
+ if (baseLayer) {
+ if (this._baseLayers && this._baseLayers.length) {
+ this._map.imageryLayers.removeAll()
+ }
+ if (!Array.isArray(baseLayer)) {
+ baseLayer = [baseLayer]
+ }
+ baseLayer.forEach((item) => {
+ this._baseLayers.push(this._map.imageryLayers.addImageryProvider(item))
+ })
+ }
+ return this
+ }
+}
+
+Widget.registerType('hawkeye_map')
+
+export default HawkeyeMap
diff --git a/src/modules/widget/type/LoadingMask.js b/src/modules/widget/type/LoadingMask.js
new file mode 100644
index 00000000..dae382be
--- /dev/null
+++ b/src/modules/widget/type/LoadingMask.js
@@ -0,0 +1,55 @@
+/**
+ * @Author: Liquid
+ * @Date: 2021-03-02 13:38:48
+ */
+
+import Widget from '../Widget'
+import State from '../../state/State'
+import { DomUtil } from '../../utils'
+
+class LoadingMask extends Widget {
+ constructor() {
+ super()
+ this._wrapper = DomUtil.create('div', 'dc-loading-mask')
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Widget.getWidgetType('loading_mask')
+ }
+
+ /**
+ *
+ * @private
+ */
+ _installHook() {
+ Object.defineProperty(this._viewer, 'loadingMask', {
+ value: this,
+ writable: false,
+ })
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountContent() {
+ let el = DomUtil.parseDom(
+ `
+
+
+
+
+
+ `,
+ true,
+ 'loading'
+ )
+ this._wrapper.appendChild(el)
+ this._ready = true
+ }
+}
+
+Widget.registerType('loading_mask')
+
+export default LoadingMask
diff --git a/src/modules/widget/type/LocationBar.js b/src/modules/widget/type/LocationBar.js
new file mode 100644
index 00000000..093fd305
--- /dev/null
+++ b/src/modules/widget/type/LocationBar.js
@@ -0,0 +1,143 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-03-04 18:02:32
+ */
+
+import { Cesium } from '../../../namespace'
+import Widget from '../Widget'
+import State from '../../state/State'
+import { DomUtil } from '../../utils'
+import { MouseEventType, SceneEventType } from '../../event'
+
+class LocationBar extends Widget {
+ constructor() {
+ super()
+ this._wrapper = DomUtil.create('div', 'dc-location-bar')
+ this._mouseEl = undefined
+ this._cameraEl = undefined
+ this._fpsEl = undefined
+ this._msEl = undefined
+ this._lastMouseSampleTime = Cesium.getTimestamp()
+ this._lastCameraSampleTime = Cesium.getTimestamp()
+ this._lastFpsSampleTime = Cesium.getTimestamp()
+ this._lastMsSampleTime = Cesium.getTimestamp()
+ this._fpsFrameCount = 0
+ this._msFrameCount = 0
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Widget.getWidgetType('location_bar')
+ }
+
+ /**
+ *
+ * @private
+ */
+ _installHook() {
+ Object.defineProperty(this._viewer, 'locationBar', {
+ value: this,
+ writable: false,
+ })
+ }
+
+ /**
+ *
+ * @private
+ */
+ _bindEvent() {
+ this._viewer.on(MouseEventType.MOUSE_MOVE, this._onMove, this)
+ this._viewer.on(SceneEventType.CAMERA_CHANGED, this._onCameraChanged, this)
+ this._viewer.on(SceneEventType.POST_UPDATE, this._onPostUpdate, this)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _unbindEvent() {
+ this._viewer.off(MouseEventType.MOUSE_MOVE, this._onMove, this)
+ this._viewer.off(SceneEventType.CAMERA_CHANGED, this._onCameraChanged, this)
+ this._viewer.off(SceneEventType.POST_UPDATE, this._onPostUpdate, this)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountContent() {
+ this._mouseEl = DomUtil.create('div', 'mouse-bar', this._wrapper)
+ this._cameraEl = DomUtil.create('div', 'camera-bar', this._wrapper)
+ this._msEl = DomUtil.create('div', 'ms-bar', this._wrapper)
+ this._fpsEl = DomUtil.create('div', 'fps-bar', this._wrapper)
+ this._ready = true
+ }
+
+ /**
+ *
+ * @param e
+ * @private
+ */
+ _onMove(e) {
+ let now = Cesium.getTimestamp()
+ if (now < this._lastMouseSampleTime + 300) {
+ return
+ }
+
+ let position = e.wgs84SurfacePosition
+ this._mouseEl.innerHTML = `
+ 经度:${position?.lng.toFixed(8) || Number.NaN}
+ 纬度:${position?.lat.toFixed(8) || Number.NaN}
+ 海拔:${position?.alt.toFixed(2) || Number.NaN} 米 `
+ this._lastMouseSampleTime = now
+ }
+
+ /**
+ *
+ * @private
+ */
+ _onCameraChanged() {
+ let now = Cesium.getTimestamp()
+ if (now < this._lastCameraSampleTime + 300) {
+ return
+ }
+ let cameraPosition = this._viewer.cameraPosition
+ this._cameraEl.innerHTML = `
+ 视角:${(+cameraPosition.pitch).toFixed(2)}
+ 视高:${(+cameraPosition.alt).toFixed(2)} 米
+ `
+ this._lastCameraSampleTime = now
+ }
+
+ /**
+ *
+ * @private
+ */
+ _onPostUpdate() {
+ let now = Cesium.getTimestamp()
+
+ // ms
+ this._msFrameCount++
+ let msElapsedTime = now - this._lastMsSampleTime
+ if (msElapsedTime > 200) {
+ let ms = (msElapsedTime / this._msFrameCount).toFixed(2)
+ this._msEl.innerHTML = `${ms} MS `
+ this._lastMsSampleTime = now
+ this._msFrameCount = 0
+ }
+
+ // fps
+ this._fpsFrameCount++
+ let fpsElapsedTime = now - this._lastFpsSampleTime
+ if (fpsElapsedTime > 1000) {
+ let fps = ((this._fpsFrameCount * 1000) / fpsElapsedTime) | 0
+ this._fpsEl.innerHTML = `${fps} FPS `
+ this._lastFpsSampleTime = now
+ this._fpsFrameCount = 0
+ }
+ }
+}
+
+Widget.registerType('location_bar')
+
+export default LocationBar
diff --git a/src/modules/widget/type/MapSplit.js b/src/modules/widget/type/MapSplit.js
new file mode 100644
index 00000000..7ddef8a5
--- /dev/null
+++ b/src/modules/widget/type/MapSplit.js
@@ -0,0 +1,131 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-03-04 15:38:40
+ */
+
+import { Cesium } from '../../../namespace'
+import State from '../../state/State'
+import Icons from '../../icons'
+import { DomUtil } from '../../utils'
+import Widget from '../Widget'
+
+class MapSplit extends Widget {
+ constructor() {
+ super()
+ this._wrapper = DomUtil.create('div', 'dc-slider')
+ this._baseLayer = undefined
+ this._moveActive = false
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Widget.getWidgetType('map_split')
+ }
+
+ /**
+ *
+ * @private
+ */
+ _installHook() {
+ Object.defineProperty(this._viewer, 'mapSplit', {
+ value: this,
+ writable: false,
+ })
+ }
+
+ /**
+ *
+ * @private
+ */
+ _bindEvent() {
+ this._viewer.scene.splitPosition = 0.5
+ this._wrapper.style.left = '50%'
+ }
+
+ /**
+ *
+ * @private
+ */
+ _unbindEvent() {
+ if (this._baseLayer) {
+ this._viewer.scene.splitPosition =
+ this._baseLayer.splitDirection > 0 ? 1 : 0
+ } else {
+ this._viewer.scene.splitPosition = 0
+ }
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountContent() {
+ let splitter = DomUtil.parseDom(Icons.splitter, true, 'splitter')
+ this._wrapper.appendChild(splitter)
+ let handler = new Cesium.ScreenSpaceEventHandler(splitter)
+ let self = this
+ handler.setInputAction(() => {
+ self._moveActive = true
+ }, Cesium.ScreenSpaceEventType.LEFT_DOWN)
+ handler.setInputAction(() => {
+ self._moveActive = true
+ }, Cesium.ScreenSpaceEventType.PINCH_START)
+
+ handler.setInputAction((movement) => {
+ self._moveHandler(movement)
+ }, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
+
+ handler.setInputAction((movement) => {
+ self._moveHandler(movement)
+ }, Cesium.ScreenSpaceEventType.PINCH_MOVE)
+
+ handler.setInputAction(() => {
+ self._moveActive = false
+ }, Cesium.ScreenSpaceEventType.LEFT_UP)
+ handler.setInputAction(() => {
+ self._moveActive = false
+ }, Cesium.ScreenSpaceEventType.PINCH_END)
+ this._ready = true
+ }
+
+ /**
+ *
+ * @param movement
+ * @private
+ */
+ _moveHandler(movement) {
+ if (!this._moveActive || !this._enable) {
+ return
+ }
+ let relativeOffset = movement.endPosition.x
+ let splitPosition =
+ (this._wrapper.offsetLeft + relativeOffset) /
+ this._wrapper.parentElement.offsetWidth
+ this._wrapper.style.left = 100.0 * splitPosition + '%'
+ this._viewer.scene.splitPosition = splitPosition
+ }
+
+ /**
+ *
+ * @param baseLayer
+ * @param splitDirection
+ * @returns {MapSplit}
+ */
+ addBaseLayer(baseLayer, splitDirection = 1) {
+ if (!this._viewer || !this._enable) {
+ return this
+ }
+ if (baseLayer) {
+ this._baseLayer && this._viewer.imageryLayers.remove(this._baseLayer)
+ this._baseLayer = this._viewer.imageryLayers.addImageryProvider(baseLayer)
+ this._baseLayer.splitDirection = splitDirection || 0
+ this._viewer.scene.splitPosition =
+ this._wrapper.offsetLeft / this._wrapper.parentElement.offsetWidth
+ }
+ return this
+ }
+}
+
+Widget.registerType('map_split')
+
+export default MapSplit
diff --git a/src/modules/widget/type/MapSwitch.js b/src/modules/widget/type/MapSwitch.js
new file mode 100644
index 00000000..7a6be20c
--- /dev/null
+++ b/src/modules/widget/type/MapSwitch.js
@@ -0,0 +1,98 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-02-11 18:34:46
+ */
+
+import Widget from '../Widget'
+import State from '../../state/State'
+import { DomUtil } from '../../utils'
+
+class MapSwitch extends Widget {
+ constructor() {
+ super()
+ this._wrapper = DomUtil.create('div', 'dc-map-switch')
+ this._config = undefined
+ this._cache = []
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Widget.getWidgetType('map_switch')
+ }
+
+ /**
+ * Override the superclass function
+ * @private
+ */
+ _enableHook() {
+ !this._wrapper.parentNode &&
+ this._viewer &&
+ this._viewer.dcContainer.appendChild(this._wrapper)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _installHook() {
+ Object.defineProperty(this._viewer, 'mapSwitch', {
+ value: this,
+ writable: false,
+ })
+ this.enable = true
+ let self = this
+ this._wrapper.onmouseover = () => {
+ let width = 80
+ let rightMargin = 5
+ if (self._cache.length > 0) {
+ width = self._cache.length * (width + rightMargin) - rightMargin
+ }
+ this._wrapper.style.width = `${width}px`
+ }
+ this._wrapper.onmouseout = () => {
+ self._wrapper.style.width = `80px`
+ }
+ }
+
+ _addItem(map) {
+ let mapEl = DomUtil.create('div', 'map-item', this._wrapper)
+ let index = this._cache.length ? this._cache.length - 1 : 0
+ index === 0 && DomUtil.addClass(mapEl, 'active')
+ mapEl.setAttribute('data-index', String(index))
+ mapEl.onclick = (e) => {
+ let old = document.getElementsByClassName('map-item active')
+ if (old && old.length) {
+ old[0].className = 'map-item'
+ }
+ if (this._viewer) {
+ e.target.className = 'map-item active'
+ this._viewer.changeBaseLayer(+e.target.getAttribute('data-index') || 0)
+ }
+ }
+ if (map.iconUrl) {
+ mapEl.style.cssText = `
+ background:url(${map.iconUrl});
+ `
+ }
+ let span = DomUtil.create('span', '', mapEl)
+ span.innerHTML = map.name || '地图'
+ }
+
+ /**
+ * add map
+ * @param map
+ */
+ addMap(map = {}) {
+ if (this._enable) {
+ this._cache.push(map)
+ this._addItem(map)
+ if (this._cache.length > 1) {
+ this._wrapper.style.visibility = 'visible'
+ }
+ }
+ }
+}
+
+Widget.registerType('map_switch')
+
+export default MapSwitch
diff --git a/src/modules/widget/type/Popup.js b/src/modules/widget/type/Popup.js
new file mode 100644
index 00000000..d4bbf598
--- /dev/null
+++ b/src/modules/widget/type/Popup.js
@@ -0,0 +1,154 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-01-15 19:16:45
+ */
+
+import { Cesium } from '../../../namespace'
+import Widget from '../Widget'
+import State from '../../state/State'
+import { DomUtil } from '../../utils'
+
+class Popup extends Widget {
+ constructor() {
+ super()
+ this._wrapper = DomUtil.create('div', 'dc-popup')
+ this._config = { customClass: '' }
+ this._position = undefined
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Widget.getWidgetType('popup')
+ }
+
+ set config(config) {
+ this._config = config
+ config.customClass && this._setCustomClass()
+ }
+
+ /**
+ * binds event
+ * @private
+ */
+ _bindEvent() {
+ if (this._viewer && this._wrapper) {
+ let self = this
+ let scene = this._viewer.scene
+ scene.postRender.addEventListener(() => {
+ if (
+ self._position &&
+ self._enable &&
+ self._updateWindowCoord &&
+ self._wrapper.style.visibility === 'visible'
+ ) {
+ let windowCoord = Cesium.SceneTransforms.wgs84ToWindowCoordinates(
+ scene,
+ self._position
+ )
+ windowCoord && self._updateWindowCoord(windowCoord)
+ }
+ })
+ }
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountContent() {
+ this._wrapper.style.visibility = 'hidden'
+ }
+
+ /**
+ *
+ * @private
+ */
+ _installHook() {
+ this.enable = true
+ this._bindEvent()
+ Object.defineProperty(this._viewer, 'popup', {
+ value: this,
+ writable: false,
+ })
+ }
+
+ /**
+ *
+ * @param windowCoord
+ * @private
+ */
+ _updateWindowCoord(windowCoord) {
+ let x = windowCoord.x - this._wrapper.offsetWidth / 2
+ let y = windowCoord.y - this._wrapper.offsetHeight
+
+ if (this._config.position === 'topleft') {
+ x = windowCoord.x - this._wrapper.offsetWidth
+ y = windowCoord.y - this._wrapper.offsetHeight
+ } else if (this._config.position === 'topright') {
+ x = windowCoord.x
+ y = windowCoord.y - this._wrapper.offsetHeight
+ } else if (this._config.position === 'bottomleft') {
+ x = windowCoord.x - this._wrapper.offsetWidth
+ y = windowCoord.y
+ } else if (this._config.position === 'bottomright') {
+ x = windowCoord.x
+ y = windowCoord.y
+ }
+
+ this._wrapper.style.cssText = `
+ visibility:visible;
+ z-index:1;
+ transform:translate3d(${Math.round(x)}px,${Math.round(y)}px, 0);
+ `
+ }
+
+ /**
+ *
+ * @private
+ */
+ _setCustomClass() {
+ DomUtil.setClass(this._wrapper, `dc-popup ${this._config.customClass}`)
+ }
+
+ /**
+ * Setting wrapper
+ * @param wrapper
+ * @returns {Widget}
+ */
+ setWrapper(wrapper) {
+ if (wrapper && wrapper instanceof Element) {
+ this._wrapper = wrapper
+ DomUtil.addClass(this._wrapper, 'dc-popup')
+ }
+ return this
+ }
+
+ /**
+ *
+ * Setting widget position
+ * @param {*} position
+ *
+ */
+ setPosition(position) {
+ this._position = position
+ this._wrapper &&
+ (this._wrapper.style.cssText = `
+ visibility:visible;
+ `)
+ return this
+ }
+
+ /**
+ *
+ * @param {*} position
+ * @param {*} content
+ */
+ showAt(position, content) {
+ this.setPosition(position).setContent(content)
+ return this
+ }
+}
+
+Widget.registerType('popup')
+
+export default Popup
diff --git a/src/modules/widget/type/SceneSplit.js b/src/modules/widget/type/SceneSplit.js
new file mode 100644
index 00000000..7b856be4
--- /dev/null
+++ b/src/modules/widget/type/SceneSplit.js
@@ -0,0 +1,142 @@
+/**
+ * @Author: Caven
+ * @Date: 2022-05-03 20:29:52
+ */
+
+import { Cesium } from '../../../namespace'
+import Widget from '../Widget'
+import State from '../../state/State'
+import Icons from '../../icons'
+import { DomUtil } from '../../utils'
+
+class SceneSplit extends Widget {
+ constructor() {
+ super()
+ this._wrapper = DomUtil.create('div', 'dc-slider')
+ this._tileset = undefined
+ this._baseLayer = undefined
+ this._moveActive = false
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Widget.getWidgetType('scene_split')
+ }
+
+ /**
+ *
+ * @private
+ */
+ _installHook() {
+ Object.defineProperty(this._viewer, 'sceneSplit', {
+ value: this,
+ writable: false,
+ })
+ }
+
+ /**
+ *
+ * @private
+ */
+ _bindEvent() {
+ this._viewer.scene.splitPosition = 0.5
+ this._wrapper.style.left = '50%'
+ }
+
+ /**
+ *
+ * @private
+ */
+ _unbindEvent() {
+ this._viewer.scene.splitPosition = 1
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountContent() {
+ let splitter = DomUtil.parseDom(Icons.splitter, true, 'splitter')
+ this._wrapper.appendChild(splitter)
+ let handler = new Cesium.ScreenSpaceEventHandler(splitter)
+ let self = this
+ handler.setInputAction(() => {
+ self._moveActive = true
+ }, Cesium.ScreenSpaceEventType.LEFT_DOWN)
+ handler.setInputAction(() => {
+ self._moveActive = true
+ }, Cesium.ScreenSpaceEventType.PINCH_START)
+
+ handler.setInputAction((movement) => {
+ self._moveHandler(movement)
+ }, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
+
+ handler.setInputAction((movement) => {
+ self._moveHandler(movement)
+ }, Cesium.ScreenSpaceEventType.PINCH_MOVE)
+
+ handler.setInputAction(() => {
+ self._moveActive = false
+ }, Cesium.ScreenSpaceEventType.LEFT_UP)
+ handler.setInputAction(() => {
+ self._moveActive = false
+ }, Cesium.ScreenSpaceEventType.PINCH_END)
+ this._ready = true
+ }
+
+ /**
+ *
+ * @param movement
+ * @private
+ */
+ _moveHandler(movement) {
+ if (!this._moveActive || !this._enable) {
+ return
+ }
+ let relativeOffset = movement.endPosition.x
+ let splitPosition =
+ (this._wrapper.offsetLeft + relativeOffset) /
+ this._wrapper.parentElement.offsetWidth
+ this._wrapper.style.left = 100.0 * splitPosition + '%'
+ this._viewer.scene.splitPosition = splitPosition
+ }
+
+ /**
+ *
+ * @param tileset
+ * @return {SceneSplit}
+ */
+ addTileset(tileset) {
+ if (!this._viewer || !this._enable) {
+ return this
+ }
+ if (tileset) {
+ this._tileset && this._viewer.scene.primitives.remove(this._tileset)
+ this._tileset = this._viewer.scene.primitives.add(
+ tileset.delegate || tileset
+ )
+ }
+ return this
+ }
+
+ /**
+ *
+ * @param baseLayer
+ * @returns {SceneSplit}
+ */
+ addBaseLayer(baseLayer) {
+ if (!this._viewer || !this._enable) {
+ return this
+ }
+ if (baseLayer) {
+ this._baseLayer && this._viewer.imageryLayers.remove(this._baseLayer)
+ this._baseLayer = this._viewer.imageryLayers.addImageryProvider(baseLayer)
+ this._baseLayer.splitDirection = 1
+ }
+ return this
+ }
+}
+
+Widget.registerType('scene_split')
+
+export default SceneSplit
diff --git a/src/modules/widget/type/TilesetSplit.js b/src/modules/widget/type/TilesetSplit.js
new file mode 100644
index 00000000..94503afa
--- /dev/null
+++ b/src/modules/widget/type/TilesetSplit.js
@@ -0,0 +1,131 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-03-04 15:38:40
+ */
+
+import { Cesium } from '../../../namespace'
+import Widget from '../Widget'
+import State from '../../state/State'
+import Icons from '../../icons'
+import { DomUtil } from '../../utils'
+
+class TilesetSplit extends Widget {
+ constructor() {
+ super()
+ this._wrapper = DomUtil.create('div', 'dc-slider')
+ this._tileset = undefined
+ this._moveActive = false
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Widget.getWidgetType('tileset_split')
+ }
+
+ /**
+ *
+ * @private
+ */
+ _installHook() {
+ Object.defineProperty(this._viewer, 'tilesetSplit', {
+ value: this,
+ writable: false,
+ })
+ }
+
+ /**
+ *
+ * @private
+ */
+ _bindEvent() {
+ this._viewer.scene.splitPosition = 0.5
+ this._wrapper.style.left = '50%'
+ }
+
+ /**
+ *
+ * @private
+ */
+ _unbindEvent() {
+ if (this._tileset) {
+ this._viewer.scene.splitPosition =
+ this._tileset.splitDirection > 0 ? 1 : 0
+ } else {
+ this._viewer.scene.splitPosition = 0
+ }
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountContent() {
+ let splitter = DomUtil.parseDom(Icons.splitter, true, 'splitter')
+ this._wrapper.appendChild(splitter)
+ let handler = new Cesium.ScreenSpaceEventHandler(splitter)
+ let self = this
+ handler.setInputAction(() => {
+ self._moveActive = true
+ }, Cesium.ScreenSpaceEventType.LEFT_DOWN)
+ handler.setInputAction(() => {
+ self._moveActive = true
+ }, Cesium.ScreenSpaceEventType.PINCH_START)
+
+ handler.setInputAction((movement) => {
+ self._moveHandler(movement)
+ }, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
+
+ handler.setInputAction((movement) => {
+ self._moveHandler(movement)
+ }, Cesium.ScreenSpaceEventType.PINCH_MOVE)
+
+ handler.setInputAction(() => {
+ self._moveActive = false
+ }, Cesium.ScreenSpaceEventType.LEFT_UP)
+ handler.setInputAction(() => {
+ self._moveActive = false
+ }, Cesium.ScreenSpaceEventType.PINCH_END)
+ this._ready = true
+ }
+
+ /**
+ *
+ * @param movement
+ * @private
+ */
+ _moveHandler(movement) {
+ if (!this._moveActive || !this._enable) {
+ return
+ }
+ let relativeOffset = movement.endPosition.x
+ let splitPosition =
+ (this._wrapper.offsetLeft + relativeOffset) /
+ this._wrapper.parentElement.offsetWidth
+ this._wrapper.style.left = 100.0 * splitPosition + '%'
+ this._viewer.scene.splitPosition = splitPosition
+ }
+
+ /**
+ *
+ * @param tileset
+ * @return {TilesetSplit}
+ */
+ addTileset(tileset) {
+ if (!this._viewer || !this._enable) {
+ return this
+ }
+ if (tileset) {
+ this._tileset && this._viewer.scene.primitives.remove(this._tileset)
+ this._tileset = this._viewer.scene.primitives.add(
+ tileset.delegate || tileset
+ )
+ this._viewer.scene.splitPosition =
+ this._wrapper.offsetLeft / this._wrapper.parentElement.offsetWidth
+ }
+ return this
+ }
+}
+
+Widget.registerType('tileset_split')
+
+export default TilesetSplit
diff --git a/src/modules/widget/type/Tooltip.js b/src/modules/widget/type/Tooltip.js
new file mode 100644
index 00000000..b3612ed0
--- /dev/null
+++ b/src/modules/widget/type/Tooltip.js
@@ -0,0 +1,67 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-02-01 12:07:54
+ */
+
+import Widget from '../Widget'
+import State from '../../state/State'
+import { DomUtil } from '../../utils'
+
+class Tooltip extends Widget {
+ constructor() {
+ super()
+ this._wrapper = DomUtil.create('div', 'dc-tool-tip')
+ this._ready = true
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Widget.getWidgetType('tooltip')
+ }
+
+ /**
+ *
+ * @private
+ */
+ _installHook() {
+ Object.defineProperty(this._viewer, 'tooltip', {
+ value: this,
+ writable: false,
+ })
+ }
+
+ /**
+ *
+ * @param {*} windowCoord
+ *
+ */
+ _updateWindowCoord(windowCoord) {
+ let x = windowCoord.x + 10
+ let y = windowCoord.y - this._wrapper.offsetHeight / 2
+ this._wrapper.style.cssText = `
+ visibility:visible;
+ z-index:1;
+ transform:translate3d(${Math.round(x)}px,${Math.round(y)}px, 0);
+ `
+ }
+
+ /**
+ *
+ * @param {*} position
+ * @param {*} content
+ *
+ */
+ showAt(position, content) {
+ if (!this._enable) {
+ return this
+ }
+
+ position && this._updateWindowCoord(position)
+ this.setContent(content)
+ return this
+ }
+}
+
+Widget.registerType('tooltip')
+
+export default Tooltip
diff --git a/src/modules/widget/type/ZoomController.js b/src/modules/widget/type/ZoomController.js
new file mode 100644
index 00000000..e26148b2
--- /dev/null
+++ b/src/modules/widget/type/ZoomController.js
@@ -0,0 +1,191 @@
+/**
+ * @Author: Caven
+ * @Date: 2020-05-06 13:25:36
+ */
+
+import { Cesium } from '../../../namespace'
+import Widget from '../Widget'
+import State from '../../state/State'
+import Icons from '../../icons'
+import { DomUtil } from '../../utils'
+
+class ZoomController extends Widget {
+ constructor() {
+ super()
+ this._wrapper = DomUtil.create('div', 'dc-zoom-controller')
+ this._zoomInEl = undefined
+ this._zoomOutEl = undefined
+ this._refreshEl = undefined
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Widget.getWidgetType('zoom_controller')
+ }
+
+ /**
+ *
+ * @param scene
+ * @returns {Cartesian3}
+ * @private
+ */
+ _getCameraFocus(scene) {
+ const ray = new Cesium.Ray(
+ scene.camera.positionWC,
+ scene.camera.directionWC
+ )
+ const intersections = Cesium.IntersectionTests.rayEllipsoid(
+ ray,
+ Cesium.Ellipsoid.WGS84
+ )
+ if (intersections) {
+ return Cesium.Ray.getPoint(ray, intersections.start)
+ }
+ // Camera direction is not pointing at the globe, so use the ellipsoid horizon point as
+ // the focal point.
+ return Cesium.IntersectionTests.grazingAltitudeLocation(
+ ray,
+ Cesium.Ellipsoid.WGS84
+ )
+ }
+
+ /**
+ *
+ * @param camera
+ * @param focus
+ * @param scalar
+ * @returns {Cartesian3}
+ * @private
+ */
+ _getCameraPosition(camera, focus, scalar) {
+ const cartesian3Scratch = new Cesium.Cartesian3()
+ let direction = Cesium.Cartesian3.subtract(
+ focus,
+ camera.position,
+ cartesian3Scratch
+ )
+ let movementVector = Cesium.Cartesian3.multiplyByScalar(
+ direction,
+ scalar,
+ cartesian3Scratch
+ )
+ return Cesium.Cartesian3.add(
+ camera.position,
+ movementVector,
+ cartesian3Scratch
+ )
+ }
+
+ /**
+ *
+ * @returns {boolean}
+ * @private
+ */
+ _zoomIn() {
+ let scene = this._viewer.scene
+ let camera = scene.camera
+ let sscc = scene.screenSpaceCameraController
+ if (
+ scene.mode === Cesium.SceneMode.MORPHING ||
+ !sscc.enableInputs ||
+ scene.mode === Cesium.SceneMode.COLUMBUS_VIEW
+ ) {
+ return true
+ } else if (scene.mode === Cesium.SceneMode.SCENE2D) {
+ camera.zoomIn(camera.positionCartographic.height * 0.5)
+ } else if (scene.mode === Cesium.SceneMode.SCENE3D) {
+ let focus = this._getCameraFocus(scene)
+ let cameraPosition = this._getCameraPosition(camera, focus, 1 / 2)
+ camera.flyTo({
+ destination: cameraPosition,
+ orientation: {
+ heading: camera.heading,
+ pitch: camera.pitch,
+ roll: camera.roll,
+ },
+ duration: 0.5,
+ convert: false,
+ })
+ }
+ }
+
+ /**
+ *
+ * @private
+ */
+ _refresh() {
+ this._viewer.camera.flyHome(1.5)
+ }
+
+ /**
+ *
+ * @returns {boolean}
+ * @private
+ */
+ _zoomOut() {
+ let scene = this._viewer.scene
+ let camera = scene.camera
+ let sscc = scene.screenSpaceCameraController
+ if (
+ scene.mode === Cesium.SceneMode.MORPHING ||
+ !sscc.enableInputs ||
+ scene.mode === Cesium.SceneMode.COLUMBUS_VIEW
+ ) {
+ return true
+ } else if (scene.mode === Cesium.SceneMode.SCENE2D) {
+ camera.zoomOut(camera.positionCartographic.height)
+ } else if (scene.mode === Cesium.SceneMode.SCENE3D) {
+ let focus = this._getCameraFocus(scene)
+ let cameraPosition = this._getCameraPosition(camera, focus, -1)
+ camera.flyTo({
+ destination: cameraPosition,
+ orientation: {
+ heading: camera.heading,
+ pitch: camera.pitch,
+ roll: camera.roll,
+ },
+ duration: 0.5,
+ convert: false,
+ })
+ }
+ }
+
+ /**
+ *
+ * @private
+ */
+ _installHook() {
+ Object.defineProperty(this._viewer, 'zoomController', {
+ value: this,
+ writable: false,
+ })
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountContent() {
+ this._zoomInEl = DomUtil.parseDom(Icons.increase, true, 'zoom-in')
+ this._refreshEl = DomUtil.parseDom(Icons.refresh, true, 'refresh')
+ this._zoomOutEl = DomUtil.parseDom(Icons.decrease, true, 'zoom-out')
+ this._wrapper.appendChild(this._zoomInEl)
+ this._wrapper.appendChild(this._refreshEl)
+ this._wrapper.appendChild(this._zoomOutEl)
+ let self = this
+ this._zoomInEl.onclick = () => {
+ self._zoomIn()
+ }
+ this._refreshEl.onclick = () => {
+ self._refresh()
+ }
+ this._zoomOutEl.onclick = () => {
+ self._zoomOut()
+ }
+ this._ready = true
+ }
+}
+
+Widget.registerType('zoom_controller')
+
+export default ZoomController
diff --git a/src/modules/wind/Field.js b/src/modules/wind/Field.js
new file mode 100644
index 00000000..7cd1498a
--- /dev/null
+++ b/src/modules/wind/Field.js
@@ -0,0 +1,433 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-01-18 20:13:30
+ */
+
+import Vector from './Vector'
+
+class Field {
+ constructor(params) {
+ this.grid = []
+ this.xmin = params.xmin
+ this.xmax = params.xmax
+ this.ymin = params.ymin
+ this.ymax = params.ymax
+ this.cols = params.cols // 列数
+ this.rows = params.rows // 行数
+ this.us = params.us //
+ this.vs = params.vs
+ this.deltaX = params.deltaX // x 方向增量
+ this.deltaY = params.deltaY // y方向增量
+ if (this.deltaY < 0 && this.ymin < this.ymax) {
+ // eslint-disable-next-line no-console
+ console.warn('[wind-core]: The data is flipY')
+ } else {
+ this.ymin = Math.min(params.ymax, params.ymin)
+ this.ymax = Math.max(params.ymax, params.ymin)
+ }
+ this.isFields = true
+ let cols = Math.ceil((this.xmax - this.xmin) / params.deltaX) // 列
+ let rows = Math.ceil((this.ymax - this.ymin) / params.deltaY) // 行
+ if (cols !== this.cols || rows !== this.rows) {
+ // eslint-disable-next-line no-console
+ console.warn('[wind-core]: The data grid not equal')
+ }
+ // Math.floor(ni * Δλ) >= 360;
+ this.isContinuous = Math.floor(this.cols * params.deltaX) >= 360
+ this.wrappedX = 'wrappedX' in params ? params.wrappedX : this.xmax > 180 // [0, 360] --> [-180, 180];
+ this.grid = this.buildGrid()
+ this.range = this.calculateRange()
+ }
+ // from https://github.com/sakitam-fdd/wind-layer/blob/95368f9433/src/windy/windy.js#L110
+ buildGrid() {
+ let grid = []
+ let p = 0
+ let _a = this,
+ rows = _a.rows,
+ cols = _a.cols,
+ us = _a.us,
+ vs = _a.vs
+ for (let j = 0; j < rows; j++) {
+ let row = []
+ for (let i = 0; i < cols; i++, p++) {
+ let u = us[p]
+ let v = vs[p]
+ let valid = this.isValid(u) && this.isValid(v)
+ row[i] = valid ? new Vector(u, v) : null
+ }
+ if (this.isContinuous) {
+ row.push(row[0])
+ }
+ grid[j] = row
+ }
+ return grid
+ }
+
+ /**
+ *
+ */
+ release() {
+ this.grid = []
+ }
+
+ /***
+ *
+ * @returns {(*)[]}
+ */
+ extent() {
+ return [this.xmin, this.ymin, this.xmax, this.ymax]
+ }
+
+ /**
+ * Bilinear interpolation for Vector
+ * https://en.wikipedia.org/wiki/Bilinear_interpolation
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number[]} g00
+ * @param {Number[]} g10
+ * @param {Number[]} g01
+ * @param {Number[]} g11
+ * @returns {Vector}
+ */
+ bilinearInterpolateVector(x, y, g00, g10, g01, g11) {
+ let rx = 1 - x
+ let ry = 1 - y
+ let a = rx * ry
+ let b = x * ry
+ let c = rx * y
+ let d = x * y
+ let u = g00.u * a + g10.u * b + g01.u * c + g11.u * d
+ let v = g00.v * a + g10.v * b + g01.v * c + g11.v * d
+ return new Vector(u, v)
+ }
+
+ /**
+ * calculate vector value range
+ */
+ calculateRange() {
+ if (!this.grid || !this.grid[0]) {
+ return
+ }
+ let rows = this.grid.length
+ let cols = this.grid[0].length
+ // const vectors = [];
+ let min
+ let max
+ // @from: https://stackoverflow.com/questions/13544476/how-to-find-max-and-min-in-array-using-minimum-comparisons
+ for (let j = 0; j < rows; j++) {
+ for (let i = 0; i < cols; i++) {
+ let vec = this.grid[j][i]
+ if (vec !== null) {
+ let val = vec.m || vec.magnitude()
+ // vectors.push();
+ if (min === undefined) {
+ min = val
+ } else if (max === undefined) {
+ max = val
+ // update min max
+ // 1. Pick 2 elements(a, b), compare them. (say a > b)
+ min = Math.min(min, max)
+ max = Math.max(min, max)
+ } else {
+ // 2. Update min by comparing (min, b)
+ // 3. Update max by comparing (max, a)
+ min = Math.min(val, min)
+ max = Math.max(val, max)
+ }
+ }
+ }
+ }
+ return [min, max]
+ }
+ /**
+ *
+ * @param x
+ * @private
+ */
+ isValid(x) {
+ return x !== null && x !== undefined
+ }
+
+ getWrappedLongitudes() {
+ let xmin = this.xmin
+ let xmax = this.xmax
+ if (this.wrappedX) {
+ if (this.isContinuous) {
+ xmin = -180
+ xmax = 180
+ } else {
+ xmax = this.xmax - 360
+ xmin = this.xmin - 360
+ }
+ }
+ return [xmin, xmax]
+ }
+
+ contains(lon, lat) {
+ let _a = this.getWrappedLongitudes(),
+ xmin = _a[0],
+ xmax = _a[1]
+ let longitudeIn = lon >= xmin && lon <= xmax
+ let latitudeIn
+ if (this.deltaY >= 0) {
+ latitudeIn = lat >= this.ymin && lat <= this.ymax
+ } else {
+ latitudeIn = lat >= this.ymax && lat <= this.ymin
+ }
+ return longitudeIn && latitudeIn
+ }
+
+ /**
+ *
+ * @param a
+ * @param n
+ * @returns {number}
+ */
+ floorMod(a, n) {
+ return a - n * Math.floor(a / n)
+ }
+
+ /**
+ *
+ * @param lon
+ * @param lat
+ */
+ getDecimalIndexes(lon, lat) {
+ let i = this.floorMod(lon - this.xmin, 360) / this.deltaX // calculate longitude index in wrapped range [0, 360)
+ let j = (this.ymax - lat) / this.deltaY // calculate latitude index in direction +90 to -90
+ return [i, j]
+ }
+
+ /**
+ * Nearest value at lon-lat coordinates
+ *
+ * @param lon
+ * @param lat
+ */
+ valueAt(lon, lat) {
+ if (!this.contains(lon, lat)) {
+ return null
+ }
+ let indexes = this.getDecimalIndexes(lon, lat)
+ let ii = Math.floor(indexes[0])
+ let jj = Math.floor(indexes[1])
+ let ci = this.clampColumnIndex(ii)
+ let cj = this.clampRowIndex(jj)
+ return this.valueAtIndexes(ci, cj)
+ }
+ /**
+ * Get interpolated grid value lon-lat coordinates
+ * @param lon
+ * @param lat
+ */
+ interpolatedValueAt(lon, lat) {
+ if (!this.contains(lon, lat)) {
+ return null
+ }
+ let _a = this.getDecimalIndexes(lon, lat),
+ i = _a[0],
+ j = _a[1]
+ return this.interpolatePoint(i, j)
+ }
+
+ hasValueAt(lon, lat) {
+ let value = this.valueAt(lon, lat)
+ return value !== null
+ }
+
+ /**
+ *
+ * @param i
+ * @param j
+ */
+ interpolatePoint(i, j) {
+ // 1 2 After converting λ and φ to fractional grid indexes i and j, we find the
+ // fi i ci four points 'G' that enclose point (i, j). These points are at the four
+ // | =1.4 | corners specified by the floor and ceiling of i and j. For example, given
+ // ---G--|---G--- fj 8 i = 1.4 and j = 8.3, the four surrounding grid points are (1, 8), (2, 8),
+ // j ___|_ . | (1, 9) and (2, 9).
+ // =8.3 | |
+ // ---G------G--- cj 9 Note that for wrapped grids, the first column is duplicated as the last
+ // | | column, so the index ci can be used without taking a modulo.
+ let indexes = this.getFourSurroundingIndexes(i, j)
+ let fi = indexes[0],
+ ci = indexes[1],
+ fj = indexes[2],
+ cj = indexes[3]
+ let values = this.getFourSurroundingValues(fi, ci, fj, cj)
+ if (values) {
+ let g00 = values[0],
+ g10 = values[1],
+ g01 = values[2],
+ g11 = values[3]
+ // @ts-ignore
+ return this.bilinearInterpolateVector(i - fi, j - fj, g00, g10, g01, g11)
+ }
+ return null
+ }
+ /**
+ * Check the column index is inside the field,
+ * adjusting to min or max when needed
+ * @private
+ * @param {Number} ii - index
+ * @returns {Number} i - inside the allowed indexes
+ */
+ clampColumnIndex(ii) {
+ let i = ii
+ if (ii < 0) {
+ i = 0
+ }
+ let maxCol = this.cols - 1
+ if (ii > maxCol) {
+ i = maxCol
+ }
+ return i
+ }
+
+ /**
+ * Check the row index is inside the field,
+ * adjusting to min or max when needed
+ * @private
+ * @param {Number} jj index
+ * @returns {Number} j - inside the allowed indexes
+ */
+ clampRowIndex(jj) {
+ let j = jj
+ if (jj < 0) {
+ j = 0
+ }
+ let maxRow = this.rows - 1
+ if (jj > maxRow) {
+ j = maxRow
+ }
+ return j
+ }
+
+ /**
+ * from: https://github.com/IHCantabria/Leaflet.CanvasLayer.Field/blob/master/src/Field.js#L252
+ * @private
+ * @param {Number} i - decimal index
+ * @param {Number} j - decimal index
+ * @returns {Array} [fi, ci, fj, cj]
+ */
+ getFourSurroundingIndexes(i, j) {
+ let fi = Math.floor(i) // 左
+ let ci = fi + 1 // 右
+ // duplicate colum to simplify interpolation logic (wrapped value)
+ if (this.isContinuous && ci >= this.cols) {
+ ci = 0
+ }
+ ci = this.clampColumnIndex(ci)
+ let fj = this.clampRowIndex(Math.floor(j)) // 上 纬度方向索引(取整)
+ let cj = this.clampRowIndex(fj + 1) // 下
+ return [fi, ci, fj, cj]
+ }
+
+ /**
+ * from https://github.com/IHCantabria/Leaflet.CanvasLayer.Field/blob/master/src/Field.js#L277
+ * Get four surrounding values or null if not available,
+ * from 4 integer indexes
+ * @private
+ * @param {Number} fi
+ * @param {Number} ci
+ * @param {Number} fj
+ * @param {Number} cj
+ * @returns {Array}
+ */
+ getFourSurroundingValues(fi, ci, fj, cj) {
+ let row
+ if ((row = this.grid[fj])) {
+ let g00 = row[fi] // << left
+ let g10 = row[ci] // right >>
+ if (this.isValid(g00) && this.isValid(g10) && (row = this.grid[cj])) {
+ // lower row vv
+ let g01 = row[fi] // << left
+ let g11 = row[ci] // right >>
+ if (this.isValid(g01) && this.isValid(g11)) {
+ return [g00, g10, g01, g11] // 4 values found!
+ }
+ }
+ }
+ return null
+ }
+
+ /**
+ * Value for grid indexes
+ * @param {Number} i - column index (integer)
+ * @param {Number} j - row index (integer)
+ * @returns {Vector|Number}
+ */
+ valueAtIndexes(i, j) {
+ return this.grid[j][i] // <-- j,i !!
+ }
+
+ /**
+ * Lon-Lat for grid indexes
+ * @param {Number} i - column index (integer)
+ * @param {Number} j - row index (integer)
+ * @returns {Number[]} [lon, lat]
+ */
+ lonLatAtIndexes(i, j) {
+ let lon = this.longitudeAtX(i)
+ let lat = this.latitudeAtY(j)
+ return [lon, lat]
+ }
+
+ /**
+ * Longitude for grid-index
+ * @param {Number} i - column index (integer)
+ * @returns {Number} longitude at the center of the cell
+ */
+ longitudeAtX(i) {
+ let halfXPixel = this.deltaX / 2.0
+ let lon = this.xmin + halfXPixel + i * this.deltaX
+ if (this.wrappedX) {
+ lon = lon > 180 ? lon - 360 : lon
+ }
+ return lon
+ }
+
+ /**
+ * Latitude for grid-index
+ * @param j
+ * @returns {number}
+ */
+ latitudeAtY(j) {
+ let halfYPixel = this.deltaY / 2.0
+ return this.ymax - halfYPixel - j * this.deltaY
+ }
+
+ /**
+ *
+ * @param o
+ * @param width
+ * @param height
+ * @param unproject
+ * @returns {{}}
+ */
+ randomize(o, width, height, unproject) {
+ if (o === void 0) {
+ o = {}
+ }
+ let i = (Math.random() * (width || this.cols)) | 0
+ let j = (Math.random() * (height || this.rows)) | 0
+ let coords = unproject([i, j])
+ if (coords !== null) {
+ o.x = coords[0]
+ o.y = coords[1]
+ } else {
+ o.x = this.longitudeAtX(i)
+ o.y = this.latitudeAtY(j)
+ }
+ return o
+ }
+
+ /**
+ * check is custom field
+ */
+ checkFields() {
+ return this.isFields
+ }
+}
+
+export default Field
diff --git a/src/modules/wind/Vector.js b/src/modules/wind/Vector.js
new file mode 100644
index 00000000..4e372a07
--- /dev/null
+++ b/src/modules/wind/Vector.js
@@ -0,0 +1,44 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-01-18 20:25:48
+ */
+
+class Vector {
+ constructor(u, v) {
+ this.u = u
+ this.v = v
+ this.m = this.magnitude()
+ }
+ /**
+ * the vector value
+ * @returns {Number}
+ */
+ magnitude() {
+ return Math.sqrt(this.u * this.u + this.v * this.v)
+ }
+ /**
+ * Angle in degrees (0 to 360º) --> Towards
+ * N is 0º and E is 90º
+ * @returns {Number}
+ */
+ directionTo() {
+ let verticalAngle = Math.atan2(this.u, this.v)
+ let inDegrees = verticalAngle * (180.0 / Math.PI)
+ if (inDegrees < 0) {
+ inDegrees += 360.0
+ }
+ return inDegrees
+ }
+
+ /**
+ * Angle in degrees (0 to 360º) From x-->
+ * N is 0º and E is 90º
+ * @returns {Number}
+ */
+ directionFrom() {
+ let a = this.directionTo()
+ return (a + 180.0) % 360.0
+ }
+}
+
+export default Vector
diff --git a/src/modules/wind/WindCanvas.js b/src/modules/wind/WindCanvas.js
new file mode 100644
index 00000000..33996fb4
--- /dev/null
+++ b/src/modules/wind/WindCanvas.js
@@ -0,0 +1,356 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-01-18 17:46:40
+ */
+
+class WindCanvas {
+ constructor(ctx) {
+ this.options = {}
+ this.particles = []
+ this.ctx = ctx
+ this.animationLoop = undefined
+ this.animate = this.animate.bind(this)
+ }
+
+ /**
+ *
+ * @param m
+ * @param min
+ * @param max
+ * @param colorScale
+ * @returns {number}
+ * @private
+ */
+ _indexFor(m, min, max, colorScale) {
+ return Math.max(
+ 0,
+ Math.min(
+ colorScale.length - 1,
+ Math.round(((m - min) / (max - min)) * (colorScale.length - 1))
+ )
+ )
+ }
+
+ /**
+ *
+ * @private
+ */
+ _moveParticles() {
+ if (!this.particles || !this.particles.length) {
+ return
+ }
+ let width = this.ctx.canvas.width
+ let height = this.ctx.canvas.height
+ let particles = this.particles
+ let maxAge = this.options.maxAge
+ let velocityScale =
+ typeof this.options.velocityScale === 'function'
+ ? this.options.velocityScale()
+ : this.options.velocityScale
+ for (let i = 0; i < particles.length; i++) {
+ let particle = particles[i]
+ if (particle.age > maxAge) {
+ particle.age = 0
+ this.field.randomize(particle, width, height, this.unProject)
+ }
+ let x = particle.x
+ let y = particle.y
+ let vector = this.field.interpolatedValueAt(x, y)
+ if (vector === null) {
+ particle.age = maxAge
+ } else {
+ let xt = x + vector.u * velocityScale
+ let yt = y + vector.v * velocityScale
+ if (this.field.hasValueAt(xt, yt)) {
+ particle.xt = xt
+ particle.yt = yt
+ particle.m = vector.m
+ } else {
+ particle.x = xt
+ particle.y = yt
+ particle.age = maxAge
+ }
+ }
+ particle.age++
+ }
+ }
+
+ /**
+ *
+ * @private
+ */
+ _drawParticles() {
+ if (!this.particles || !this.particles.length) {
+ return
+ }
+ let particles = this.particles
+ let prev = this.ctx.globalCompositeOperation
+ this.ctx.globalCompositeOperation = 'destination-in'
+ this.ctx.fillRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height)
+ this.ctx.globalCompositeOperation = prev
+ this.ctx.globalAlpha = this.options.globalAlpha
+ this.ctx.fillStyle = 'rgba(0, 0, 0, ' + this.options.globalAlpha + ')'
+ this.ctx.lineWidth = this.options.lineWidth ? this.options.lineWidth : 1
+ this.ctx.strokeStyle = this.options.colorScale
+ ? this.options.colorScale
+ : '#fff'
+ let i = 0
+ let len = particles.length
+ if (this.field && len > 0) {
+ let min = void 0
+ let max = void 0
+ if (this.options.minVelocity && this.options.maxVelocity) {
+ min = this.options.minVelocity
+ max = this.options.maxVelocity
+ } else {
+ let _a = this.field.range
+ min = _a[0]
+ max = _a[1]
+ }
+ for (; i < len; i++) {
+ this[
+ this.options.useCoordsDraw
+ ? '_drawCoordsParticle'
+ : '_drawPixelParticle'
+ ](particles[i], min, max)
+ }
+ }
+ }
+
+ /**
+ *
+ * @param particle
+ * @param min
+ * @param max
+ */
+ _drawPixelParticle(particle, min, max) {
+ let pointPrev = [particle.x, particle.y]
+ let pointNext = [particle.xt, particle.yt]
+ let dx = particle.xt - particle.x
+ let dy = particle.yt - particle.y
+ if (dx * dx + dy * dy > 20 * 20) {
+ return
+ }
+ if (
+ pointNext &&
+ pointPrev &&
+ pointNext[0] &&
+ pointNext[1] &&
+ pointPrev[0] &&
+ pointPrev[1] &&
+ particle.age <= this.options.maxAge
+ ) {
+ this._drawStroke(pointPrev, pointNext, particle, min, max)
+ }
+ }
+
+ /**
+ *
+ * @param particle
+ * @param min
+ * @param max
+ */
+ _drawCoordsParticle(particle, min, max) {
+ let source = [particle.x, particle.y]
+ let target = [particle.xt, particle.yt]
+ if (
+ target &&
+ source &&
+ target[0] &&
+ target[1] &&
+ source[0] &&
+ source[1] &&
+ this.intersectsCoordinate(target) &&
+ particle.age <= this.options.maxAge
+ ) {
+ let pointPrev = this.project(source)
+ let pointNext = this.project(target)
+ this._drawStroke(pointPrev, pointNext, particle, min, max)
+ }
+ }
+
+ /**
+ *
+ * @param pointPrev
+ * @param pointNext
+ * @param particle
+ * @param min
+ * @param max
+ * @private
+ */
+ _drawStroke(pointPrev, pointNext, particle, min, max) {
+ if (pointPrev && pointNext) {
+ this.ctx.beginPath()
+ this.ctx.moveTo(pointPrev[0], pointPrev[1])
+ this.ctx.lineTo(pointNext[0], pointNext[1])
+ if (typeof this.options.colorScale === 'function') {
+ this.ctx.strokeStyle = this.options.colorScale(particle.m)
+ } else if (Array.isArray(this.options.colorScale)) {
+ let colorIdx = this._indexFor(
+ particle.m,
+ min,
+ max,
+ this.options.colorScale
+ )
+ this.ctx.strokeStyle = this.options.colorScale[colorIdx]
+ }
+ if (typeof this.options.lineWidth === 'function') {
+ this.ctx.lineWidth = this.options.lineWidth(particle.m)
+ }
+ particle.x = particle.xt
+ particle.y = particle.yt
+ this.ctx.stroke()
+ }
+ }
+
+ /**
+ *
+ * @returns {[]|*[]}
+ * @private
+ */
+ _prepareParticlePaths() {
+ let width = this.ctx.canvas.width
+ let height = this.ctx.canvas.height
+ let particleCount =
+ typeof this.options.paths === 'function'
+ ? this.options.paths(this)
+ : this.options.paths
+ let particles = []
+ if (!this.field) {
+ return []
+ }
+ for (let i = 0; i < particleCount; i++) {
+ particles.push(
+ this.field.randomize(
+ {
+ age: Math.floor(Math.random() * this.options.maxAge)
+ },
+ width,
+ height,
+ this.unProject
+ )
+ )
+ }
+ return particles
+ }
+
+ /**
+ *
+ */
+ project() {
+ throw new Error('project must be overriden')
+ }
+
+ /**
+ *
+ */
+ unProject() {
+ throw new Error('unProject must be overriden')
+ }
+
+ /**
+ *
+ * @param coordinates
+ */
+ intersectsCoordinate(coordinates) {
+ throw new Error('must be override')
+ }
+
+ /**
+ *
+ */
+ prerender() {
+ if (!this.field) {
+ return
+ }
+ this.particles = this._prepareParticlePaths()
+ if (!this.starting && !this.forceStop) {
+ this.starting = true
+ this._then = Date.now()
+ this.animate()
+ }
+ }
+
+ /**
+ *
+ * @returns {WindCanvas}
+ */
+ render() {
+ this._moveParticles()
+ this._drawParticles()
+ return this
+ }
+
+ clearCanvas() {
+ this.stop()
+ this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height)
+ this.forceStop = false
+ }
+
+ /**
+ *
+ */
+ start() {
+ this.starting = true
+ this.forceStop = false
+ this._then = Date.now()
+ this.animate()
+ }
+
+ /**
+ *
+ */
+ stop() {
+ cancelAnimationFrame(this.animationLoop)
+ this.starting = false
+ this.forceStop = true
+ }
+
+ /**
+ *
+ */
+ animate() {
+ if (this.animationLoop) {
+ cancelAnimationFrame(this.animationLoop)
+ }
+ this.animationLoop = requestAnimationFrame(this.animate)
+ let now = Date.now()
+ let delta = now - this._then
+ if (delta > this.options.frameRate) {
+ this._then = now - (delta % this.options.frameRate)
+ this.render()
+ }
+ }
+
+ /**
+ *
+ * @param field
+ * @returns {WindCanvas}
+ */
+ setData(field) {
+ this.field = field
+ return this
+ }
+
+ /**
+ *
+ * @param options
+ * @returns {WindCanvas}
+ */
+ setOptions(options) {
+ this.options = options
+ if (!this.options?.maxAge && this.options?.particleAge) {
+ this.options.maxAge = Number(this.options.particleAge)
+ }
+ if (!this.options?.paths && this.options?.particleMultiplier) {
+ this.options.paths = Math.round(
+ this.options.width *
+ this.options.height *
+ Number(this.options.particleMultiplier)
+ )
+ }
+ return this
+ }
+}
+
+export default WindCanvas
diff --git a/src/modules/wind/WindLayer.js b/src/modules/wind/WindLayer.js
new file mode 100644
index 00000000..281cc37f
--- /dev/null
+++ b/src/modules/wind/WindLayer.js
@@ -0,0 +1,243 @@
+/**
+ * @Author: Caven
+ * @Date: 2021-01-18 20:13:30
+ */
+
+import { Cesium } from '../../namespace'
+import { Layer } from '../layer'
+import State from '../state/State'
+import Field from './Field'
+import WindCanvas from './WindCanvas'
+
+const DEF_OPTS = {
+ globalAlpha: 0.9,
+ lineWidth: 1,
+ colorScale: '#fff',
+ velocityScale: 1 / 25,
+ maxAge: 90,
+ paths: 800,
+ frameRate: 20,
+ useCoordsDraw: true,
+ gpet: true,
+}
+
+class WindLayer extends Layer {
+ constructor(id, options = {}) {
+ super(id)
+ this._options = {
+ ...DEF_OPTS,
+ ...options,
+ }
+ this._data = undefined
+ this._canvas = document.createElement('canvas')
+ this._state = State.INITIALIZED
+ }
+
+ get type() {
+ return Layer.getLayerType('wind')
+ }
+
+ set show(show) {
+ this._show = show
+ this._canvas.style.visibility = show ? 'visible' : 'hidden'
+ }
+
+ get show() {
+ return this._show
+ }
+
+ /**
+ *
+ * @param data
+ * @returns {Field|undefined}
+ * @private
+ */
+ _formatData(data) {
+ let uComp
+ let vComp
+ data.forEach(function (record) {
+ switch (
+ record.header.parameterCategory +
+ ',' +
+ record.header.parameterNumber
+ ) {
+ case '1,2':
+ case '2,2':
+ uComp = record
+ break
+ case '1,3':
+ case '2,3':
+ vComp = record
+ break
+ }
+ })
+ if (!vComp || !uComp) {
+ return undefined
+ }
+ let header = uComp.header
+ return new Field({
+ xmin: header.lo1,
+ ymin: header.la1,
+ xmax: header.lo2,
+ ymax: header.la2,
+ deltaX: header.dx,
+ deltaY: header.dy,
+ cols: header.nx,
+ rows: header.ny,
+ us: uComp.data,
+ vs: vComp.data,
+ })
+ }
+
+ /**
+ *
+ * @private
+ */
+ _mountCanvas() {
+ if (!this._viewer || !this._canvas) {
+ return
+ }
+ this._canvas.style.cssText =
+ 'position:absolute; left:0; top:0;user-select:none;pointer-events: none;'
+ this._canvas.className = 'dc-wind-layer'
+ const { width, height } = this._viewer.canvas
+ this._canvas.width = width
+ this._canvas.height = height
+ this._canvas.style.width = width + 'px'
+ this._canvas.style.height = height + 'px'
+ this._viewer.dcContainer.appendChild(this._canvas)
+ }
+
+ /**
+ *
+ * @private
+ */
+ _addedHook() {
+ let scene = this._viewer.scene
+ let camera = this._viewer.camera
+ let ellipsoid = Cesium.Ellipsoid.WGS84
+ this._delegate.intersectsCoordinate = (coordinate) => {
+ if (scene.mode === Cesium.SceneMode.SCENE3D) {
+ let occluder = new Cesium.EllipsoidalOccluder(
+ ellipsoid,
+ camera.position
+ )
+ let point = Cesium.Cartesian3.fromDegrees(coordinate[0], coordinate[1])
+ return occluder.isPointVisible(point)
+ } else if (scene.mode === Cesium.SceneMode.SCENE2D) {
+ return !(coordinate[0] === -180 || coordinate[0] === 180)
+ }
+ return true
+ }
+
+ this._delegate.project = (coordinate) => {
+ let position = Cesium.Cartesian3.fromDegrees(coordinate[0], coordinate[1])
+ let coord = Cesium.SceneTransforms.wgs84ToWindowCoordinates(
+ scene,
+ position
+ )
+ return [coord.x, coord.y]
+ }
+
+ this._delegate.unProject = (pixel) => {
+ let pick = new Cesium.Cartesian2(pixel[0], pixel[1])
+ let cartesian = undefined
+ if (scene.mode === Cesium.SceneMode.SCENE3D) {
+ cartesian = scene.globe.pick(camera.getPickRay(pick), scene)
+ } else {
+ cartesian = scene.camera.pickEllipsoid(pick, ellipsoid)
+ }
+ if (!cartesian) {
+ return null
+ }
+ let cartographic = ellipsoid.cartesianToCartographic(cartesian)
+ let lat = Cesium.Math.toDegrees(cartographic.latitude)
+ let lng = Cesium.Math.toDegrees(cartographic.longitude)
+ return [lng, lat]
+ }
+ }
+
+ /**
+ *
+ * @param viewer
+ * @private
+ */
+ _onAdd(viewer) {
+ this._viewer = viewer
+ this._mountCanvas()
+ let ctx = this._canvas.getContext('2d')
+ if (!this._delegate) {
+ this._delegate = new WindCanvas(ctx)
+ this._delegate.setOptions(this._options)
+ this._addedHook()
+ }
+ if (this._data) {
+ this._delegate.setData(this._data)
+ this._delegate.prerender()
+ this._delegate.render()
+ }
+ }
+
+ /**
+ *
+ * @private
+ */
+ _onRemove() {
+ if (this._delegate) {
+ this._delegate.stop()
+ }
+ if (this._canvas) {
+ this._viewer.dcContainer.removeChild(this._canvas)
+ }
+ delete this._canvas
+ }
+
+ /**
+ *
+ * @param data
+ * @param options
+ * @returns {WindLayer}
+ */
+ setData(data, options) {
+ if (data && data.checkFields && data.checkFields()) {
+ this._data = data
+ } else if (Array.isArray(data)) {
+ this._data = this._formatData(data)
+ }
+ if (this._delegate) {
+ this._delegate.setData(this._data)
+ if (options) {
+ this._options = {
+ ...this._options,
+ ...options,
+ }
+ this._delegate.setOptions(this._options)
+ }
+ this._delegate.prerender()
+ this._delegate.render()
+ }
+ return this
+ }
+
+ /**
+ *
+ * @param options
+ * @returns {WindLayer}
+ */
+ setOptions(options) {
+ this._options = {
+ ...this._options,
+ ...options,
+ }
+ if (this._delegate) {
+ this._delegate.setOptions(this._options)
+ this._delegate.prerender()
+ this._delegate.render()
+ }
+ return this
+ }
+}
+
+Layer.registerType('wind')
+
+export default WindLayer
diff --git a/src/namespace/index.js b/src/namespace/index.js
new file mode 100644
index 00000000..88f32bbf
--- /dev/null
+++ b/src/namespace/index.js
@@ -0,0 +1,14 @@
+/**
+ @author : Caven Chen
+ @date : 2023-05-06
+ */
+
+import { getLib } from '../global-api/lib-utils.js'
+
+export const Cesium = getLib('Cesium')
+
+export const turf = getLib('turf')
+
+export const mapv = getLib('mapv')
+
+export const echarts = getLib('echarts')
diff --git a/src/themes/index.js b/src/themes/index.js
new file mode 100644
index 00000000..9070275a
--- /dev/null
+++ b/src/themes/index.js
@@ -0,0 +1,6 @@
+/**
+ @author : Caven Chen
+ @date : 2023-05-08
+ */
+
+import './index.scss'
diff --git a/src/themes/index.scss b/src/themes/index.scss
new file mode 100644
index 00000000..25b2e797
--- /dev/null
+++ b/src/themes/index.scss
@@ -0,0 +1,57 @@
+@charset "UTF-8";
+
+* {
+ padding: 0;
+ margin: 0;
+}
+
+.dc-container {
+ overflow: hidden;
+ display: block;
+}
+
+.dc-viewer {
+ font-family: sans-serif;
+ font-size: 16px;
+ overflow: hidden;
+ display: block;
+ position: relative;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+}
+
+.dc-viewer-widget-container {
+ width: 100%;
+ height: 100%;
+}
+
+.dc-widget {
+ font-family: sans-serif;
+ font-size: 16px;
+ overflow: hidden;
+ display: block;
+ position: relative;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+}
+
+.dc-widget,
+.dc-widget canvas {
+ width: 100%;
+ height: 100%;
+ touch-action: none;
+}
+
+.div-icon {
+ user-select: none;
+ background-color: #fff;
+ padding: 2px 2px;
+ border-radius: 4px;
+ &:hover {
+ cursor: pointer;
+ }
+}