├── .gitignore ├── README.md ├── babel.config.js ├── jsconfig.json ├── package.json ├── public ├── favicon.ico ├── icons │ ├── 1.png │ ├── 2.png │ └── 3.png ├── index.html └── model │ ├── Duck.glb │ ├── Wood_Tower.glb │ ├── default.png │ └── tower.png ├── release.js ├── src ├── App.vue ├── assets │ ├── css │ │ ├── demo.css │ │ ├── iconfont.css │ │ ├── iconfont.eot │ │ ├── iconfont.js │ │ ├── iconfont.json │ │ ├── iconfont.svg │ │ ├── iconfont.ttf │ │ ├── iconfont.woff │ │ ├── iconfont.woff2 │ │ └── theme │ │ │ ├── dark.scss │ │ │ └── default.scss │ ├── images │ │ ├── 0.png │ │ ├── icon │ │ │ ├── file.png │ │ │ ├── label.png │ │ │ ├── marker.png │ │ │ ├── polygon.png │ │ │ └── polyline.png │ │ └── nothmub.jpg │ ├── js │ │ └── iconfont.js │ └── logo.png ├── cdn.js ├── components │ ├── cesiumDrawViewer.vue │ ├── layerManager.vue │ └── markerViewer.vue ├── core │ ├── Graphic.js │ ├── GraphicManager.js │ ├── GraphicType.js │ └── MarkerManager.js ├── js │ └── utils.js └── main.js ├── vue.config.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | yarn.lock 15 | pnpm-debug.log* 16 | 17 | # Editor directories and files 18 | .idea 19 | .vscode 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Cesium-Draw 3 | 4 | 基于Vue 3.x开发的Cesium基础标绘插件,支持交互式添加BillBoard、Polyline、Polygon、Label和Model 5 | 6 | Vue 2.x请访问 https://github.com/xtfge/cesium-draw/tree/cesium-draw-vue2 7 | 8 | 核心功能: 9 | - 鼠标交互绘图 10 | - 对于Billboard、Label、Model支持图标,名称的编辑,图标可以任意扩展. 11 | - 对于Polyline和Polygon支持顶点、颜色等常见图形属性的编辑. 12 | - 支持导入、导出功能 13 | - 可以通过图层管理器管理通过该插件添加的所有图形. 14 | 15 | 兼容性 16 | - 目前已测试兼容的Cesium最低版本为Cesium@1.88,最高版本为Cesium@1.119 17 | - 版本低于1.88的版本没有经过测试 18 | ### 安装 19 | 20 | ```sh 21 | npm i cesium-draw 22 | ``` 23 | 24 | **4.0.0及以上版本为Vue3.x版本,4.0.0以下的版本为Vue2.x的版本** 25 | 26 | ### 使用 27 | ```HTML 28 | 34 | 53 | ``` 54 | **如果您是在html中通过script引用的Cesium,您需要额外配置webpack** 55 | 您需要修改`vue.config.js`, 添加以下内容 56 | ```js 57 | module.exports = defineConfig({ 58 | // ... 59 | configureWebpack: { 60 | externals: { 61 | cesium: 'Cesium' 62 | } 63 | }, 64 | // ... 65 | }); 66 | ``` 67 | 如果您使用的构建工具是`vite`,请了解vite相关配置。 68 | #### 扩展图片标记的图标 69 | ```html 70 | 71 | ``` 72 | ```js 73 | data(){ 74 | return{ 75 | images:["./static/images/markers/1.png", 76 | "./static/images/markers/2.png", 77 | "./static/images/markers/3.png", 78 | "./static/images/markers/4.png", 79 | "./static/images/markers/5.png" 80 | ] 81 | } 82 | } 83 | ``` 84 | #### 启用模型标记 85 | **你必须通过`extendMarkerModel`属性定义用于标记的模型,才可以使用模型标记。** 86 | 87 | 比如: 88 | ```html 89 | 90 | ``` 91 | ```js 92 | data(){ 93 | return{ 94 | models:[ 95 | { id: "model0", 96 | name: "木塔", 97 | thumb:'thumb.png', // 定义模型的缩略图,如果未定义,则使用默认图片 98 | url: "static/model/Wood_Tower.gltf" }, 99 | { 100 | id: "model1", 101 | name: "小人", 102 | url: "static/model/Cesium_Man.gltf" }] 103 | } 104 | } 105 | ``` 106 | #### 如何使用喜欢的主题 107 | ```js 108 | import 'cesium-draw/dist/theme/default.css' 109 | ``` 110 | 或 111 | ```js 112 | import 'cesium-draw/dist/theme/dark.css' 113 | ``` 114 | >更多主题敬请期待。 115 | #### 完整示例 116 | ```HTML 117 | 123 | 155 | ``` 156 | ### Methods 157 | - `getById(mid)` 根据id返回图形要素 158 | ### Events 159 | 事件|说明|回调 160 | ---|---|--- 161 | deleteEvent|要素删除事件|参数为删除要素的id 162 | locateEvent|要素定位事件|定位要素的id 163 | editEvent|要素编辑事件|要素的id 164 | renameEvent|要素重命名事件|两个参数,依次为要素id,要素更新前的名称 165 | selectEvent|checkbox点击事件|两个参数,依次为要素id,checkbox状态 166 | closeEvent|标绘面板关闭事件|无 167 | ### 开发&打包 168 | ```sh 169 | npm install 170 | npm run build 171 | ``` 172 | ### 打包成组件 173 | ```sh 174 | npm run lib 175 | ``` 176 | ### 效果 177 | ![avatar](https://img-blog.csdnimg.cn/20200102184048249.gif) 178 | ![avatar](https://img-blog.csdnimg.cn/2020041719034414.gif) 179 | ![avatar](https://i-blog.csdnimg.cn/direct/edb405fdfbb24d598219b704692f1db9.gif) 180 | 181 | ### Cesium交流群 182 | [![加入QQ群](https://img.shields.io/badge/%E5%B7%B2%E6%BB%A1-107615960-blue)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=bgWooLP8IhmlRV-V9ATdqEmq3oXze8uX&authKey=4ce2A9KMcoxJOpiASPIBXTNwc%2B5a3cL7n4P%2BoXD2YyJp4dR4H2BfHfqBQi4RurYP&noverify=0&group_code=107615960) [![加入QQ群](https://img.shields.io/badge/-628262010-green)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=OPOFzUro3j8kgtFYG1NU3NEammB0bTny&authKey=esRwasLjLPchfAxo6qQjYHsiWGN4%2BT32WKKjOVHyKeMr1HMvfWHl1PRmyo4zGQis&noverify=0&group_code=628262010) 183 | 184 | ### 捐赠 185 | >如果您觉得这个项目帮助到了您,您可以请作者喝一杯咖啡表示鼓励 186 | 187 | 微信收款码 -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ], 5 | plugins: ['@babel/plugin-proposal-class-properties'], 6 | } 7 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2021", 4 | "module": "esnext", 5 | "baseUrl": "./", 6 | "moduleResolution": "node", 7 | "paths": { 8 | "@/*": [ 9 | "src/*" 10 | ] 11 | }, 12 | "lib": [ 13 | "esnext", 14 | "dom", 15 | "dom.iterable", 16 | "scripthost" 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cesium-draw", 3 | "version": "4.1.0", 4 | "description": "add a mark,polyline,polygon base on Cesium and Vue 3.x", 5 | "private": false, 6 | "keyword": "Cesium Polygon Polyline mark interactive", 7 | "main": "./dist/cesiumdrawViewer.umd.min.js", 8 | "files": [ 9 | "dist", 10 | "package.json", 11 | "README.md" 12 | ], 13 | "scripts": { 14 | "serve": "vue-cli-service serve", 15 | "build": "vue-cli-service build", 16 | "lint": "vue-cli-service lint", 17 | "lib": "vue-cli-service build --target lib --name cesiumdrawViewer ./src/cdn.js" 18 | }, 19 | "dependencies": { 20 | "cesium": "^1.119.0", 21 | "core-js": "^3.8.3", 22 | "element-plus": "^2.3.5", 23 | "file-saver": "^2.0.5", 24 | "jquery": "^3.7.0", 25 | "shapefile": "^0.6.6", 26 | "vue": "^3.2.13" 27 | }, 28 | "devDependencies": { 29 | "@babel/core": "^7.21.8", 30 | "@babel/eslint-parser": "^7.12.16", 31 | "@babel/plugin-proposal-class-properties": "^7.18.6", 32 | "@babel/preset-env": "^7.21.5", 33 | "@vue/cli-plugin-babel": "~5.0.0", 34 | "@vue/cli-plugin-eslint": "~5.0.0", 35 | "@vue/cli-service": "~5.0.0", 36 | "cesium-draw": "^4.0.0", 37 | "copy-webpack-plugin": "^12.0.2", 38 | "default-passive-events": "^2.0.0", 39 | "eslint": "^8.41.0", 40 | "eslint-plugin-vue": "^9.14.0", 41 | "mini-css-extract-plugin": "^2.7.6", 42 | "sass": "^1.62.1", 43 | "sass-loader": "^13.2.2", 44 | "vue-cli-plugin-element": "~1.0.1" 45 | }, 46 | "eslintConfig": { 47 | "root": true, 48 | "env": { 49 | "node": true 50 | }, 51 | "extends": [ 52 | "plugin:vue/vue3-essential", 53 | "eslint:recommended" 54 | ], 55 | "parserOptions": { 56 | "parser": "@babel/eslint-parser" 57 | }, 58 | "globals": { 59 | "Cesium": "writable" 60 | }, 61 | "rules": {} 62 | }, 63 | "browserslist": [ 64 | "> 1%", 65 | "last 2 versions", 66 | "not dead", 67 | "not ie 11" 68 | ] 69 | } 70 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xtfge/cesium-draw/dc5b3d87046b176666bc30eebf93c830e8d2ea33/public/favicon.ico -------------------------------------------------------------------------------- /public/icons/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xtfge/cesium-draw/dc5b3d87046b176666bc30eebf93c830e8d2ea33/public/icons/1.png -------------------------------------------------------------------------------- /public/icons/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xtfge/cesium-draw/dc5b3d87046b176666bc30eebf93c830e8d2ea33/public/icons/2.png -------------------------------------------------------------------------------- /public/icons/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xtfge/cesium-draw/dc5b3d87046b176666bc30eebf93c830e8d2ea33/public/icons/3.png -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | <%= htmlWebpackPlugin.options.title %> 11 | 12 | 13 | 16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /public/model/Duck.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xtfge/cesium-draw/dc5b3d87046b176666bc30eebf93c830e8d2ea33/public/model/Duck.glb -------------------------------------------------------------------------------- /public/model/Wood_Tower.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xtfge/cesium-draw/dc5b3d87046b176666bc30eebf93c830e8d2ea33/public/model/Wood_Tower.glb -------------------------------------------------------------------------------- /public/model/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xtfge/cesium-draw/dc5b3d87046b176666bc30eebf93c830e8d2ea33/public/model/default.png -------------------------------------------------------------------------------- /public/model/tower.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xtfge/cesium-draw/dc5b3d87046b176666bc30eebf93c830e8d2ea33/public/model/tower.png -------------------------------------------------------------------------------- /release.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: zhangb 3 | * @Date: 2023-05-23 14:18:59 4 | * @E-mail: zhangb@geovis.com.cn 5 | * @LastModifiedBy: zhangb 6 | * @LastEditTime: 2023-05-23 15:56:34 7 | * @Desc: 8 | */ 9 | const fs = require('fs') 10 | const path = require('path') 11 | const buildDir = path.join(__dirname, 'dist'); 12 | const assets = 'theme'; 13 | function remove(url) { 14 | // 读取原路径 15 | const STATUS = fs.statSync(url); 16 | // 如果原路径是文件 17 | if (STATUS.isFile()) { 18 | //删除原文件 19 | fs.unlinkSync(url); 20 | 21 | //如果原路径是目录 22 | } else if (STATUS.isDirectory()) { 23 | //如果原路径是非空目录,遍历原路径 24 | //空目录时无法使用forEach 25 | fs.readdirSync(url).forEach(item => { 26 | //递归调用函数,以子文件路径为新参数 27 | remove(`${url}/${item}`); 28 | }); 29 | //删除空文件夹 30 | fs.rmdirSync(url); 31 | }; 32 | } 33 | 34 | function release() { 35 | fs.stat(buildDir, function(err, stat) { 36 | if (!err) { 37 | remove(buildDir); 38 | } 39 | fs.mkdirSync(buildDir); 40 | fs.mkdirSync(path.join(buildDir, assets)); 41 | fs.mkdirSync(path.join(buildDir, assets, 'img')); 42 | // copy(distDir, buildDir); 43 | // walkFiles(cssDir); 44 | }); 45 | } 46 | release() 47 | 48 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 129 | 130 | 181 | 187 | -------------------------------------------------------------------------------- /src/assets/css/demo.css: -------------------------------------------------------------------------------- 1 | /* Logo 字体 */ 2 | @font-face { 3 | font-family: "iconfont logo"; 4 | src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834'); 5 | src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'), 6 | url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'), 7 | url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'), 8 | url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg'); 9 | } 10 | 11 | .logo { 12 | font-family: "iconfont logo"; 13 | font-size: 160px; 14 | font-style: normal; 15 | -webkit-font-smoothing: antialiased; 16 | -moz-osx-font-smoothing: grayscale; 17 | } 18 | 19 | /* tabs */ 20 | .nav-tabs { 21 | position: relative; 22 | } 23 | 24 | .nav-tabs .nav-more { 25 | position: absolute; 26 | right: 0; 27 | bottom: 0; 28 | height: 42px; 29 | line-height: 42px; 30 | color: #666; 31 | } 32 | 33 | #tabs { 34 | border-bottom: 1px solid #eee; 35 | } 36 | 37 | #tabs li { 38 | cursor: pointer; 39 | width: 100px; 40 | height: 40px; 41 | line-height: 40px; 42 | text-align: center; 43 | font-size: 16px; 44 | border-bottom: 2px solid transparent; 45 | position: relative; 46 | z-index: 1; 47 | margin-bottom: -1px; 48 | color: #666; 49 | } 50 | 51 | 52 | #tabs .active { 53 | border-bottom-color: #f00; 54 | color: #222; 55 | } 56 | 57 | .tab-container .content { 58 | display: none; 59 | } 60 | 61 | /* 页面布局 */ 62 | .main { 63 | padding: 30px 100px; 64 | width: 960px; 65 | margin: 0 auto; 66 | } 67 | 68 | .main .logo { 69 | color: #333; 70 | text-align: left; 71 | margin-bottom: 30px; 72 | line-height: 1; 73 | height: 110px; 74 | margin-top: -50px; 75 | overflow: hidden; 76 | *zoom: 1; 77 | } 78 | 79 | .main .logo a { 80 | font-size: 160px; 81 | color: #333; 82 | } 83 | 84 | .helps { 85 | margin-top: 40px; 86 | } 87 | 88 | .helps pre { 89 | padding: 20px; 90 | margin: 10px 0; 91 | border: solid 1px #e7e1cd; 92 | background-color: #fffdef; 93 | overflow: auto; 94 | } 95 | 96 | .icon_lists { 97 | width: 100% !important; 98 | overflow: hidden; 99 | *zoom: 1; 100 | } 101 | 102 | .icon_lists li { 103 | width: 100px; 104 | margin-bottom: 10px; 105 | margin-right: 20px; 106 | text-align: center; 107 | list-style: none !important; 108 | cursor: default; 109 | } 110 | 111 | .icon_lists li .code-name { 112 | line-height: 1.2; 113 | } 114 | 115 | .icon_lists .icon { 116 | display: block; 117 | height: 100px; 118 | line-height: 100px; 119 | font-size: 42px; 120 | margin: 10px auto; 121 | color: #333; 122 | -webkit-transition: font-size 0.25s linear, width 0.25s linear; 123 | -moz-transition: font-size 0.25s linear, width 0.25s linear; 124 | transition: font-size 0.25s linear, width 0.25s linear; 125 | } 126 | 127 | .icon_lists .icon:hover { 128 | font-size: 100px; 129 | } 130 | 131 | .icon_lists .svg-icon { 132 | /* 通过设置 font-size 来改变图标大小 */ 133 | width: 1em; 134 | /* 图标和文字相邻时,垂直对齐 */ 135 | vertical-align: -0.15em; 136 | /* 通过设置 color 来改变 SVG 的颜色/fill */ 137 | fill: currentColor; 138 | /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示 139 | normalize.css 中也包含这行 */ 140 | overflow: hidden; 141 | } 142 | 143 | .icon_lists li .name, 144 | .icon_lists li .code-name { 145 | color: #666; 146 | } 147 | 148 | /* markdown 样式 */ 149 | .markdown { 150 | color: #666; 151 | font-size: 14px; 152 | line-height: 1.8; 153 | } 154 | 155 | .highlight { 156 | line-height: 1.5; 157 | } 158 | 159 | .markdown img { 160 | vertical-align: middle; 161 | max-width: 100%; 162 | } 163 | 164 | .markdown h1 { 165 | color: #404040; 166 | font-weight: 500; 167 | line-height: 40px; 168 | margin-bottom: 24px; 169 | } 170 | 171 | .markdown h2, 172 | .markdown h3, 173 | .markdown h4, 174 | .markdown h5, 175 | .markdown h6 { 176 | color: #404040; 177 | margin: 1.6em 0 0.6em 0; 178 | font-weight: 500; 179 | clear: both; 180 | } 181 | 182 | .markdown h1 { 183 | font-size: 28px; 184 | } 185 | 186 | .markdown h2 { 187 | font-size: 22px; 188 | } 189 | 190 | .markdown h3 { 191 | font-size: 16px; 192 | } 193 | 194 | .markdown h4 { 195 | font-size: 14px; 196 | } 197 | 198 | .markdown h5 { 199 | font-size: 12px; 200 | } 201 | 202 | .markdown h6 { 203 | font-size: 12px; 204 | } 205 | 206 | .markdown hr { 207 | height: 1px; 208 | border: 0; 209 | background: #e9e9e9; 210 | margin: 16px 0; 211 | clear: both; 212 | } 213 | 214 | .markdown p { 215 | margin: 1em 0; 216 | } 217 | 218 | .markdown>p, 219 | .markdown>blockquote, 220 | .markdown>.highlight, 221 | .markdown>ol, 222 | .markdown>ul { 223 | width: 80%; 224 | } 225 | 226 | .markdown ul>li { 227 | list-style: circle; 228 | } 229 | 230 | .markdown>ul li, 231 | .markdown blockquote ul>li { 232 | margin-left: 20px; 233 | padding-left: 4px; 234 | } 235 | 236 | .markdown>ul li p, 237 | .markdown>ol li p { 238 | margin: 0.6em 0; 239 | } 240 | 241 | .markdown ol>li { 242 | list-style: decimal; 243 | } 244 | 245 | .markdown>ol li, 246 | .markdown blockquote ol>li { 247 | margin-left: 20px; 248 | padding-left: 4px; 249 | } 250 | 251 | .markdown code { 252 | margin: 0 3px; 253 | padding: 0 5px; 254 | background: #eee; 255 | border-radius: 3px; 256 | } 257 | 258 | .markdown strong, 259 | .markdown b { 260 | font-weight: 600; 261 | } 262 | 263 | .markdown>table { 264 | border-collapse: collapse; 265 | border-spacing: 0px; 266 | empty-cells: show; 267 | border: 1px solid #e9e9e9; 268 | width: 95%; 269 | margin-bottom: 24px; 270 | } 271 | 272 | .markdown>table th { 273 | white-space: nowrap; 274 | color: #333; 275 | font-weight: 600; 276 | } 277 | 278 | .markdown>table th, 279 | .markdown>table td { 280 | border: 1px solid #e9e9e9; 281 | padding: 8px 16px; 282 | text-align: left; 283 | } 284 | 285 | .markdown>table th { 286 | background: #F7F7F7; 287 | } 288 | 289 | .markdown blockquote { 290 | font-size: 90%; 291 | color: #999; 292 | border-left: 4px solid #e9e9e9; 293 | padding-left: 0.8em; 294 | margin: 1em 0; 295 | } 296 | 297 | .markdown blockquote p { 298 | margin: 0; 299 | } 300 | 301 | .markdown .anchor { 302 | opacity: 0; 303 | transition: opacity 0.3s ease; 304 | margin-left: 8px; 305 | } 306 | 307 | .markdown .waiting { 308 | color: #ccc; 309 | } 310 | 311 | .markdown h1:hover .anchor, 312 | .markdown h2:hover .anchor, 313 | .markdown h3:hover .anchor, 314 | .markdown h4:hover .anchor, 315 | .markdown h5:hover .anchor, 316 | .markdown h6:hover .anchor { 317 | opacity: 1; 318 | display: inline-block; 319 | } 320 | 321 | .markdown>br, 322 | .markdown>p>br { 323 | clear: both; 324 | } 325 | 326 | 327 | .hljs { 328 | display: block; 329 | background: white; 330 | padding: 0.5em; 331 | color: #333333; 332 | overflow-x: auto; 333 | } 334 | 335 | .hljs-comment, 336 | .hljs-meta { 337 | color: #969896; 338 | } 339 | 340 | .hljs-string, 341 | .hljs-variable, 342 | .hljs-template-variable, 343 | .hljs-strong, 344 | .hljs-emphasis, 345 | .hljs-quote { 346 | color: #df5000; 347 | } 348 | 349 | .hljs-keyword, 350 | .hljs-selector-tag, 351 | .hljs-type { 352 | color: #a71d5d; 353 | } 354 | 355 | .hljs-literal, 356 | .hljs-symbol, 357 | .hljs-bullet, 358 | .hljs-attribute { 359 | color: #0086b3; 360 | } 361 | 362 | .hljs-section, 363 | .hljs-name { 364 | color: #63a35c; 365 | } 366 | 367 | .hljs-tag { 368 | color: #333333; 369 | } 370 | 371 | .hljs-title, 372 | .hljs-attr, 373 | .hljs-selector-id, 374 | .hljs-selector-class, 375 | .hljs-selector-attr, 376 | .hljs-selector-pseudo { 377 | color: #795da3; 378 | } 379 | 380 | .hljs-addition { 381 | color: #55a532; 382 | background-color: #eaffea; 383 | } 384 | 385 | .hljs-deletion { 386 | color: #bd2c00; 387 | background-color: #ffecec; 388 | } 389 | 390 | .hljs-link { 391 | text-decoration: underline; 392 | } 393 | 394 | /* 代码高亮 */ 395 | /* PrismJS 1.15.0 396 | https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */ 397 | /** 398 | * prism.js default theme for JavaScript, CSS and HTML 399 | * Based on dabblet (http://dabblet.com) 400 | * @author Lea Verou 401 | */ 402 | code[class*="language-"], 403 | pre[class*="language-"] { 404 | color: black; 405 | background: none; 406 | text-shadow: 0 1px white; 407 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; 408 | text-align: left; 409 | white-space: pre; 410 | word-spacing: normal; 411 | word-break: normal; 412 | word-wrap: normal; 413 | line-height: 1.5; 414 | 415 | -moz-tab-size: 4; 416 | -o-tab-size: 4; 417 | tab-size: 4; 418 | 419 | -webkit-hyphens: none; 420 | -moz-hyphens: none; 421 | -ms-hyphens: none; 422 | hyphens: none; 423 | } 424 | 425 | pre[class*="language-"]::-moz-selection, 426 | pre[class*="language-"] ::-moz-selection, 427 | code[class*="language-"]::-moz-selection, 428 | code[class*="language-"] ::-moz-selection { 429 | text-shadow: none; 430 | background: #b3d4fc; 431 | } 432 | 433 | pre[class*="language-"]::selection, 434 | pre[class*="language-"] ::selection, 435 | code[class*="language-"]::selection, 436 | code[class*="language-"] ::selection { 437 | text-shadow: none; 438 | background: #b3d4fc; 439 | } 440 | 441 | @media print { 442 | 443 | code[class*="language-"], 444 | pre[class*="language-"] { 445 | text-shadow: none; 446 | } 447 | } 448 | 449 | /* Code blocks */ 450 | pre[class*="language-"] { 451 | padding: 1em; 452 | margin: .5em 0; 453 | overflow: auto; 454 | } 455 | 456 | :not(pre)>code[class*="language-"], 457 | pre[class*="language-"] { 458 | background: #f5f2f0; 459 | } 460 | 461 | /* Inline code */ 462 | :not(pre)>code[class*="language-"] { 463 | padding: .1em; 464 | border-radius: .3em; 465 | white-space: normal; 466 | } 467 | 468 | .token.comment, 469 | .token.prolog, 470 | .token.doctype, 471 | .token.cdata { 472 | color: slategray; 473 | } 474 | 475 | .token.punctuation { 476 | color: #999; 477 | } 478 | 479 | .namespace { 480 | opacity: .7; 481 | } 482 | 483 | .token.property, 484 | .token.tag, 485 | .token.boolean, 486 | .token.number, 487 | .token.constant, 488 | .token.symbol, 489 | .token.deleted { 490 | color: #905; 491 | } 492 | 493 | .token.selector, 494 | .token.attr-name, 495 | .token.string, 496 | .token.char, 497 | .token.builtin, 498 | .token.inserted { 499 | color: #690; 500 | } 501 | 502 | .token.operator, 503 | .token.entity, 504 | .token.url, 505 | .language-css .token.string, 506 | .style .token.string { 507 | color: #9a6e3a; 508 | background: hsla(0, 0%, 100%, .5); 509 | } 510 | 511 | .token.atrule, 512 | .token.attr-value, 513 | .token.keyword { 514 | color: #07a; 515 | } 516 | 517 | .token.function, 518 | .token.class-name { 519 | color: #DD4A68; 520 | } 521 | 522 | .token.regex, 523 | .token.important, 524 | .token.variable { 525 | color: #e90; 526 | } 527 | 528 | .token.important, 529 | .token.bold { 530 | font-weight: bold; 531 | } 532 | 533 | .token.italic { 534 | font-style: italic; 535 | } 536 | 537 | .token.entity { 538 | cursor: help; 539 | } 540 | -------------------------------------------------------------------------------- /src/assets/css/iconfont.css: -------------------------------------------------------------------------------- 1 | @font-face {font-family: "cesiumDrawFont"; 2 | src: url('iconfont.eot?t=1577957761281'); /* IE9 */ 3 | src: url('iconfont.eot?t=1577957761281#iefix') format('embedded-opentype'), /* IE6-IE8 */ 4 | url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAxgAAsAAAAAFoAAAAwQAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCESgqdVJZ0ATYCJANECyQABCAFhG0HgTMbMxJRlJBWHNlPTZ6kiFRIBamogCpYK9iXwxX0CRsowKwAAAgCnm318wJQAWsz271K9350p0O4rRsLaqMsPtlXmY2+oJ+YH0ncZlA/ZzTf0fvKZCgvm1kcZlpP5mS/Kin5Jl8tWsdbvMDsCa+ClwqAmf+31urN4c27Dqah0RIhzt+9A+bv7L0bTCyZhAaJkAnJxCOhQiJED6nzCCUCAWsrB7YCxCpO2ezXuTsE4COT9iD9hi45QIJGoaDVtgWsGUhKSegWF4HHYlpW1TnIJAw88igpACfEry++oVZIQGIo1J6mo2k2kPy5/DysPDEPq9RgDtpfBjCaChRQDkCP9L2t1l1BEws3FF9kjGEAJholBTEvIDZqdambZ8olXC/+wTOx8ODFh40fB5cAQYAwUZqD3YvVQcDn0oRAwOcaqQsDiUKYIG3CAkF4QBBeEIQPBGGDIBwQhAuC8IPEIgDiJoIgiBBImwjDIROwrAq5ANYB9Nlg3InS941X9qKQmARB8KEw3jAuJTGL47UK89uTTrY+QibJplQsXuX1+rz5+SQpNHcJKLSKI0k9RVjjF6ltKsbm4qT2CqgZ43aRn0wyPJ5dxuhRxA8yWJWbX+A6mubL3J4ChcmTWJIr5ucV5BfYrq5j/fXI6qvza2+lzP1vGdkw/ngtZ9Fqx5+vr0enmqyFsSvKjYUQZ9Py0wB5oMVEK6E9btmu0acoXQO9nIKxMMloY8ckBgsKTPafShJZCWNlz8jEGahM/3U0CegDz4rg1FtPioSVmEhF4WRUIByegBI6JQR9hrKPjgwonkw7KbJ5Z47KMoUyhI0JkpL7pPgKanwmiuDt/d7I/IP2YIkBXAQDzmmkLuwFjhZZ/9l26FuKoKZXdPipzMDJpUIIujmbP/EMJAQHmQHeVzBEghVoSFySv6NYOu6VFCnGO4Y07CnrJEPCSn2h5GdKZMi1kSh62fyBaHa2Ot22lT1pslox5XBhYZFdeGxMjJ/m79OQwhqGDsZwaLMTYq4cuWOLSUuKZV01fX6mqFAMA1qasaekSAoYCgZocYnSwdZVUrH22vwTm2z19cjGG4urry6svebLNxYf43ukDq2l/Xojb8m64fur2X10G0IuJFiArmwtvKGEC53BmsWaJ5Fut3tHgR3TSQmtOLJPYjJJLkwqSWqMUC1nCEyRxB6QLNitk3njRsW6SEhptrzSlihno3EqT9vn8p3MVU/n6/vdwSTx3sLLBq8c3Gp4+RxJ2l1ofh/JdZ1q8jZ1558eCBpMh7oADkxIlBGP0s85EHD44sbqArsohA9wNEXCOO3Mvf7BkkaYc0pE1hL2pCgcTZbvWxaTFPHGNows8R+9sBfYtxLcA6M9URDJMq8SCGbfh6O3ncYbcDoeJj5OSX5y3hlLp8svVIYI9CsrI4X3cGcYzLv3ZsSUc9g7EL4TObl4CNjG/u1z2UhZ1id3OQjKtNiXlLzHSxJ9a2nRT9zIwpN9tzsQdlzAWUxSaF5NTPlXxCoDuezozJTdPzB/RlvcV6k/66SUYJTGO8n1Vq1+OL8amXLo/gVfz+SPISri1xvfeLFonBLWYqYz904n9HDe35fp3+uD/MknD898KxPWn7F3FKxYh/bNX8pYs4WNHs+DKydwpMda2u9BSW1jx172Z94bvXt5b+IxhCCOwkC2aJbagKPvX1mOk5vHj7/pS69xBZEt17ZOPLm0KsVFwbL+wgG5u/3IG4JuXPgtQ2/BtuEbNxZUcy/4qlPYCB2AAfu0mYoceG/zbJ2egqqD+sVqDs7R6800nLoYzlYbVqaOFUmQajfMMaqgGTAlFSrDitQxOncs1O2DqzaVGi7VU3ZmUclJQMsGk8gxaZId0SxkaT1l5cOZjz+a5+geh6enmwYa03FiyDjQlG58f9teh4Kpaqp368Wtcyh1fdWhXS7iRinw4gcP4kRJwN7W4/Y7kfAujYkyokd0krdDyeS2i3BiTwL54rsHbfF2069BXFgyGSaAD1NRPYlSMbUyRIWMzXxyo4PvYeJMpSRLpN7aqMfx1bhJlIyGLxwOop8nF+D4B0W7g94nJcuJ8w1596Ff+xnV5LlhFGi/f7uwHsOlyXtL6Nr4vr5rUXBv71yp58sDPY3+fLMjg8w99OWNC2SjnZQ35aMb8ZJ7H1x16B4HRMozeCtDJHAaSJAcdt7H+Wg3yD+m/LhKTHUjQKleJUm5ysVlfmLN2aPSYgNrAHLLeQ+2xGyaduJQ+6Pad2n7pQ9TT25yNmz5o2Pu304jAjCQUORInXN9g+TjXTOEG1tSX2W/N6iXKFrHsgPVZU1Lj+9E3x8QGwzcL4LJY5qWNisN0SR2yQixTefjmpYltxa8bASMDh70ZjNFTw28Z8Lh2St2hBUVztaoZrMLi8J2rMh6tjw1+H/5CHTVXbLRLpwwHlIj7b1XoiNk9fLl1X3WYXd/cW4Ojdi5c4RqC/XpbewGueSNNy5WtbV+BqPJZDTi+FDM8kn9alu43ycvaN9XxwLCkv/ikPDa6f9kq/+rNH3HXPdMlU1rglrt/x3nJeOx/ifTQanuPfDAvTrpnjd3bkzm7lWruuWA9ev37j14cPvRvgy5vDt527Y1uj1S6TVZwOsdVjffVEHCEoAueb2+/djffz0k7JdAvGzw6G7QjcekJz3gUYuAyNHTX5ekzHjt069pX2RNKSny1Gya8Zah2GIdmn+h1+Tji40UHq+2Fx2SsuT67V9veyPpIr0yjA5TJmuXnNzn9x03aD455uGRWVLtptuXOJg6sGyzWHFkxywdgb/2xiW2lXfqqRXSZOG1N8LeujaXPW65PUW2bL5+invxTLkYuDJ8lFalHVlbW64tr60ZPplaO6pL5xmHSIE69cRFOfURyQoSpP5or8gR5mci8liyUs4ZEiQ1p55aInhCIF7MW2IgC9WS7O2dfjT/kVQr/+A84Hgdxqx10PlgH9M8Fn1f/UDwexWNJzumGTTr+ZL9yy6/ZOF8p27Xpuhdtn9CqFyJJDeZHgW6ulVqXeCMBCKH0hXpFZUqIXdv0OXjF6Ny6sKjbzwlXqwc6RSoCEo3EXtae4Cf2V12f48fCdOHotT1n9DidE7GR1d4fNzg/ImNzqZeSPx9hhgHhrRAn7Y9+TosWPn58PqNO7VMQN62IyS+/GDzDppF3DJa2qNNyogY/falmlq/h/m/7OleXNE9vVdWj6tyX5ExY9DhoYBM8SJn5jae4ypC93WemDBlZHTst1luwTCHujrPwF1VTVsPNu5ZE9mkFXX3jePv3U3Yg8IUvcP6IGwYK9RGw5iWoWFk4NR+7/P2TZ7/er65IfpSXTQszEzFhiXFP52a9PKLfQs7qY7ybO02kvE2w1oN1wsNFXqQ3FxEL2Qt7S3VFZjPogq91SQYOmVaiJNBXb9natQuIgFil3Q+moXXG/WgSqz3Tx2rRDFV1aPTUY+Ly46ij17T1Wu1jZdHI+ogfsiVdOcMVvtweVle7nVfPaRy+c824nEercZkxP7FPlkQpp7ibNz7J7zVAGX8N4/9j7L07bq3D4rBjdQp5UIPEpUtoBcoDdBi8MYv42RySM9TMGLV8CG3buU+icwfY/9Q9+gTYiyIr5CAT5dCDKi8MPePqyc4m/q8ekJOJRYZVOEhn2i7cmpgU09NPHRRH2UY17aJgwNFaC9QynCPCoJspBI/D1BFkHMMht09apDIO2oSFCj1ERE+m7QpFAWyVA6aeWijLRV0gdXlZPxUXpa0zoJsid3MFeNizzzIFReU0iGBwdVaI3RCbh9LFJeyoTzP0AznctAGb2/QbnfRbs5VCK18YD7Pu2ODghjbkoFWlwPI6IMDYoYPA9ugsVARvQArLpwwY/PIai+fBYSlBDtmuJYeF9HzgHAUUzBxmhACCXYgRs3p1OO5dCymFJZQeFHBQMOMWrpwQGMQGRC7rnJB47bPVAjECk8g+VtUuIklSIzEuGqBx9cdr73Gy9er2l5IiIQSWhjCFJbwCK/wCbuPuWXhFwERRNSIBuDbmisgRzjMXBHkCkezQDvBQYerFIrdLnsF63ISNmiHPCTsLusDq8CgrYBvbeg0OyDucNlR4rKMvcAJiQKH28XxBCyPlsOYAjvErS67ixO7Svg/EwEAAAAA') format('woff2'), 5 | url('iconfont.woff?t=1577957761281') format('woff'), 6 | url('iconfont.ttf?t=1577957761281') format('truetype') 7 | } 8 | 9 | .cesiumDrawFont { 10 | font-family: "cesiumDrawFont" !important; 11 | font-size: 16px; 12 | font-style: normal; 13 | -webkit-font-smoothing: antialiased; 14 | -moz-osx-font-smoothing: grayscale; 15 | } 16 | 17 | .iconlayer:before { 18 | content: "\e602"; 19 | } 20 | 21 | .iconmarker:before { 22 | content: "\e604"; 23 | } 24 | 25 | .iconlabel:before { 26 | content: "\e605"; 27 | } 28 | 29 | .iconremove:before { 30 | content: "\e606"; 31 | } 32 | 33 | .iconpolygon:before { 34 | content: "\e607"; 35 | } 36 | 37 | .icondelete:before { 38 | content: "\e608"; 39 | } 40 | 41 | .iconlocate:before { 42 | content: "\e609"; 43 | } 44 | 45 | .iconedit:before { 46 | content: "\e60a"; 47 | } 48 | 49 | .iconrename:before { 50 | content: "\e60b"; 51 | } 52 | 53 | .iconmodel:before { 54 | content: "\e60c"; 55 | } 56 | 57 | .iconpolyline:before { 58 | content: "\e60d"; 59 | } 60 | 61 | .iconimport:before { 62 | content: "\e60f"; 63 | } 64 | 65 | .iconexport:before { 66 | content: "\e610"; 67 | } 68 | 69 | .iconfile:before { 70 | content: "\e60e"; 71 | } 72 | 73 | .iconcolor:before { 74 | content: "\e611"; 75 | } 76 | 77 | .iconoutline:before { 78 | content: "\e612"; 79 | } 80 | 81 | -------------------------------------------------------------------------------- /src/assets/css/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xtfge/cesium-draw/dc5b3d87046b176666bc30eebf93c830e8d2ea33/src/assets/css/iconfont.eot -------------------------------------------------------------------------------- /src/assets/css/iconfont.js: -------------------------------------------------------------------------------- 1 | !function(m){var c,o='',a=(c=document.getElementsByTagName("script"))[c.length-1].getAttribute("data-injectcss");if(a&&!m.__iconfont__svg__cssinject__){m.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(c){console&&console.log(c)}}!function(c){if(document.addEventListener)if(~["complete","loaded","interactive"].indexOf(document.readyState))setTimeout(c,0);else{var a=function(){document.removeEventListener("DOMContentLoaded",a,!1),c()};document.addEventListener("DOMContentLoaded",a,!1)}else document.attachEvent&&(l=c,e=m.document,h=!1,(o=function(){try{e.documentElement.doScroll("left")}catch(c){return void setTimeout(o,50)}t()})(),e.onreadystatechange=function(){"complete"==e.readyState&&(e.onreadystatechange=null,t())});function t(){h||(h=!0,l())}var l,e,h,o}(function(){var c,a,t,l,e,h;(c=document.createElement("div")).innerHTML=o,o=null,(a=c.getElementsByTagName("svg")[0])&&(a.setAttribute("aria-hidden","true"),a.style.position="absolute",a.style.width=0,a.style.height=0,a.style.overflow="hidden",t=a,(l=document.body).firstChild?(e=t,(h=l.firstChild).parentNode.insertBefore(e,h)):l.appendChild(t))})}(window); -------------------------------------------------------------------------------- /src/assets/css/iconfont.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "1590177", 3 | "name": "my-project", 4 | "font_family": "iconfont", 5 | "css_prefix_text": "icon", 6 | "description": "", 7 | "glyphs": [ 8 | { 9 | "icon_id": "12597587", 10 | "name": "layer", 11 | "font_class": "layer", 12 | "unicode": "e602", 13 | "unicode_decimal": 58882 14 | }, 15 | { 16 | "icon_id": "12597662", 17 | "name": "marker", 18 | "font_class": "marker", 19 | "unicode": "e604", 20 | "unicode_decimal": 58884 21 | }, 22 | { 23 | "icon_id": "12597676", 24 | "name": "label", 25 | "font_class": "label", 26 | "unicode": "e605", 27 | "unicode_decimal": 58885 28 | }, 29 | { 30 | "icon_id": "12597679", 31 | "name": "remove", 32 | "font_class": "remove", 33 | "unicode": "e606", 34 | "unicode_decimal": 58886 35 | }, 36 | { 37 | "icon_id": "12597688", 38 | "name": "polygon", 39 | "font_class": "polygon", 40 | "unicode": "e607", 41 | "unicode_decimal": 58887 42 | }, 43 | { 44 | "icon_id": "12597851", 45 | "name": "删除/数字面板编辑态", 46 | "font_class": "delete", 47 | "unicode": "e608", 48 | "unicode_decimal": 58888 49 | }, 50 | { 51 | "icon_id": "12597852", 52 | "name": "复位", 53 | "font_class": "locate", 54 | "unicode": "e609", 55 | "unicode_decimal": 58889 56 | }, 57 | { 58 | "icon_id": "12597853", 59 | "name": "编辑", 60 | "font_class": "edit", 61 | "unicode": "e60a", 62 | "unicode_decimal": 58890 63 | }, 64 | { 65 | "icon_id": "12597854", 66 | "name": "重命名", 67 | "font_class": "rename", 68 | "unicode": "e60b", 69 | "unicode_decimal": 58891 70 | }, 71 | { 72 | "icon_id": "12597984", 73 | "name": "model", 74 | "font_class": "model", 75 | "unicode": "e60c", 76 | "unicode_decimal": 58892 77 | }, 78 | { 79 | "icon_id": "12597985", 80 | "name": "polyline", 81 | "font_class": "polyline", 82 | "unicode": "e60d", 83 | "unicode_decimal": 58893 84 | }, 85 | { 86 | "icon_id": "12598030", 87 | "name": "import", 88 | "font_class": "import", 89 | "unicode": "e60f", 90 | "unicode_decimal": 58895 91 | }, 92 | { 93 | "icon_id": "12598051", 94 | "name": "export (1)", 95 | "font_class": "export", 96 | "unicode": "e610", 97 | "unicode_decimal": 58896 98 | }, 99 | { 100 | "icon_id": "12598567", 101 | "name": "文件", 102 | "font_class": "file", 103 | "unicode": "e60e", 104 | "unicode_decimal": 58894 105 | }, 106 | { 107 | "icon_id": "12598568", 108 | "name": "颜色选择器 (1)", 109 | "font_class": "color", 110 | "unicode": "e611", 111 | "unicode_decimal": 58897 112 | }, 113 | { 114 | "icon_id": "12636469", 115 | "name": "边框", 116 | "font_class": "outline", 117 | "unicode": "e612", 118 | "unicode_decimal": 58898 119 | } 120 | ] 121 | } 122 | -------------------------------------------------------------------------------- /src/assets/css/iconfont.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | Created by iconfont 9 | 10 | 11 | 12 | 13 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /src/assets/css/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xtfge/cesium-draw/dc5b3d87046b176666bc30eebf93c830e8d2ea33/src/assets/css/iconfont.ttf -------------------------------------------------------------------------------- /src/assets/css/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xtfge/cesium-draw/dc5b3d87046b176666bc30eebf93c830e8d2ea33/src/assets/css/iconfont.woff -------------------------------------------------------------------------------- /src/assets/css/iconfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xtfge/cesium-draw/dc5b3d87046b176666bc30eebf93c830e8d2ea33/src/assets/css/iconfont.woff2 -------------------------------------------------------------------------------- /src/assets/css/theme/dark.scss: -------------------------------------------------------------------------------- 1 | $bg-color:#171E26; 2 | $color:#3A89DD; 3 | $color-l:#C7E9FA; 4 | $font-color:$color; 5 | $selected-color:#409EFF; 6 | $border-color:#E4E7ED; 7 | $devision-color:#DCDFE6; 8 | $b-radius:4px; 9 | $draw-panel-width:400px; 10 | $hover-color:#409EFF; 11 | $title-height:32px; 12 | $item-margin:0 5px; 13 | $item-height:40px; 14 | $padding:0 5px; 15 | $font-size:12px; 16 | //跟随鼠标的提示信息 17 | .cursor-tip-class{ 18 | position : fixed; 19 | // border : 1px $border-color solid; 20 | height : 30px; 21 | line-height : 30px; 22 | padding-left : 10px; 23 | padding-right : 20px; 24 | background-color : $bg-color; 25 | color : $font-color; 26 | border-radius : $b-radius; 27 | pointer-events : none; 28 | z-index : 999; 29 | min-width: 150px; 30 | font-size: $font-size; 31 | } 32 | //关闭按钮 33 | .closebtn{ 34 | text-align: center; 35 | display: inline-block!important; 36 | font-size: $font-size; 37 | color:$color; 38 | // margin-top: 13px!important; 39 | // margin-right: 13px!important; 40 | cursor: pointer; 41 | float: right; 42 | &:hover { 43 | color: #FF0000; 44 | } 45 | } 46 | //滚动条 47 | *{ 48 | &::-webkit-scrollbar { 49 | /*滚动条整体样式*/ 50 | width: 5px; /*高宽分别对应横竖滚动条的尺寸*/ 51 | height: 1px; 52 | } 53 | &::-webkit-scrollbar-thumb { 54 | /*滚动条里面小方块*/ 55 | border-radius: 2px; 56 | box-shadow: inset 0 0 2px $color; 57 | background: $color; 58 | } 59 | &::-webkit-scrollbar-track { 60 | /*滚动条里面轨道*/ 61 | box-shadow: inset 0 0 2px $bg-color; 62 | border-radius: 2px; 63 | background: $bg-color; 64 | } 65 | } 66 | //气泡 67 | .marker-popWin-class { 68 | min-width: 200px; 69 | height: 73px; 70 | position: fixed; 71 | color: $font-color; 72 | background-color: $bg-color; 73 | text-align: left; 74 | border-radius: $b-radius; 75 | font-size: $font-size; 76 | visibility: visible; 77 | // border: 1px solid $border-color; 78 | box-sizing: border-box; 79 | padding: 0 12px; 80 | span{ 81 | margin-top: 12px; 82 | display: block; 83 | } 84 | .arrow { 85 | position: absolute; 86 | top: 73px; 87 | left: 0px; 88 | width: 0px; 89 | height: 0px; 90 | left: 50%; 91 | transform: translate(-50%, 0); 92 | border-width: 10px; 93 | border-style: solid; 94 | border-color: $bg-color transparent transparent transparent; 95 | } 96 | } 97 | 98 | -------------------------------------------------------------------------------- /src/assets/css/theme/default.scss: -------------------------------------------------------------------------------- 1 | $bg-color:#FFFFFF; 2 | $color:#000000; 3 | $color-l:#C7E9FA; 4 | $font-color:#000000; 5 | $selected-color:#409EFF; 6 | $border-color:#E4E7ED; 7 | $devision-color:#DCDFE6; 8 | $b-radius:4px; 9 | $draw-panel-width:400px; 10 | $hover-color:#409EFF; 11 | $title-height:32px; 12 | $item-margin:0 5px; 13 | $item-height:40px; 14 | $padding:0 5px; 15 | $font-size:12px; 16 | //跟随鼠标的提示信息 17 | .cursor-tip-class{ 18 | position : fixed; 19 | // border : 1px $border-color solid; 20 | height : 30px; 21 | line-height : 30px; 22 | padding-left : 10px; 23 | padding-right : 20px; 24 | background-color : $bg-color; 25 | color : $font-color; 26 | border-radius : $b-radius; 27 | pointer-events : none; 28 | z-index : 999; 29 | min-width: 150px; 30 | font-size: $font-size; 31 | } 32 | //关闭按钮 33 | .closebtn{ 34 | text-align: center; 35 | display: inline-block!important; 36 | font-size: $font-size; 37 | color:$color; 38 | // margin-top: 13px!important; 39 | // margin-right: 13px!important; 40 | cursor: pointer; 41 | float: right; 42 | &:hover { 43 | color: #FF0000; 44 | } 45 | } 46 | //滚动条 47 | *{ 48 | &::-webkit-scrollbar { 49 | /*滚动条整体样式*/ 50 | width: 5px; /*高宽分别对应横竖滚动条的尺寸*/ 51 | height: 1px; 52 | } 53 | &::-webkit-scrollbar-thumb { 54 | /*滚动条里面小方块*/ 55 | border-radius: 2px; 56 | box-shadow: inset 0 0 2px $color; 57 | background: $color; 58 | } 59 | &::-webkit-scrollbar-track { 60 | /*滚动条里面轨道*/ 61 | box-shadow: inset 0 0 2px $bg-color; 62 | border-radius: 2px; 63 | background: $bg-color; 64 | } 65 | } 66 | //气泡 67 | .marker-popWin-class { 68 | min-width: 200px; 69 | height: 73px; 70 | position: fixed; 71 | color: $font-color; 72 | background-color: $bg-color; 73 | text-align: left; 74 | border-radius: $b-radius; 75 | font-size: $font-size; 76 | visibility: visible; 77 | // border: 1px solid $border-color; 78 | box-sizing: border-box; 79 | padding: 0 12px; 80 | span{ 81 | margin-top: 12px; 82 | display: block; 83 | } 84 | .arrow { 85 | position: absolute; 86 | top: 73px; 87 | left: 0px; 88 | width: 0px; 89 | height: 0px; 90 | left: 50%; 91 | transform: translate(-50%, 0); 92 | border-width: 10px; 93 | border-style: solid; 94 | border-color: $bg-color transparent transparent transparent; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/assets/images/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xtfge/cesium-draw/dc5b3d87046b176666bc30eebf93c830e8d2ea33/src/assets/images/0.png -------------------------------------------------------------------------------- /src/assets/images/icon/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xtfge/cesium-draw/dc5b3d87046b176666bc30eebf93c830e8d2ea33/src/assets/images/icon/file.png -------------------------------------------------------------------------------- /src/assets/images/icon/label.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xtfge/cesium-draw/dc5b3d87046b176666bc30eebf93c830e8d2ea33/src/assets/images/icon/label.png -------------------------------------------------------------------------------- /src/assets/images/icon/marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xtfge/cesium-draw/dc5b3d87046b176666bc30eebf93c830e8d2ea33/src/assets/images/icon/marker.png -------------------------------------------------------------------------------- /src/assets/images/icon/polygon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xtfge/cesium-draw/dc5b3d87046b176666bc30eebf93c830e8d2ea33/src/assets/images/icon/polygon.png -------------------------------------------------------------------------------- /src/assets/images/icon/polyline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xtfge/cesium-draw/dc5b3d87046b176666bc30eebf93c830e8d2ea33/src/assets/images/icon/polyline.png -------------------------------------------------------------------------------- /src/assets/images/nothmub.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xtfge/cesium-draw/dc5b3d87046b176666bc30eebf93c830e8d2ea33/src/assets/images/nothmub.jpg -------------------------------------------------------------------------------- /src/assets/js/iconfont.js: -------------------------------------------------------------------------------- 1 | !function(d){var l,a='',i=(l=document.getElementsByTagName("script"))[l.length-1].getAttribute("data-injectcss");if(i&&!d.__iconfont__svg__cssinject__){d.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(l){console&&console.log(l)}}!function(l){if(document.addEventListener)if(~["complete","loaded","interactive"].indexOf(document.readyState))setTimeout(l,0);else{var i=function(){document.removeEventListener("DOMContentLoaded",i,!1),l()};document.addEventListener("DOMContentLoaded",i,!1)}else document.attachEvent&&(h=l,o=d.document,e=!1,(a=function(){try{o.documentElement.doScroll("left")}catch(l){return void setTimeout(a,50)}t()})(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,t())});function t(){e||(e=!0,h())}var h,o,e,a}(function(){var l,i,t,h,o,e;(l=document.createElement("div")).innerHTML=a,a=null,(i=l.getElementsByTagName("svg")[0])&&(i.setAttribute("aria-hidden","true"),i.style.position="absolute",i.style.width=0,i.style.height=0,i.style.overflow="hidden",t=i,(h=document.body).firstChild?(o=t,(e=h.firstChild).parentNode.insertBefore(o,e)):h.appendChild(t))})}(window); -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xtfge/cesium-draw/dc5b3d87046b176666bc30eebf93c830e8d2ea33/src/assets/logo.png -------------------------------------------------------------------------------- /src/cdn.js: -------------------------------------------------------------------------------- 1 | import _Vue from 'vue' 2 | import drawViewer from './components/cesiumDrawViewer' 3 | import "@/assets/css/iconfont.css" 4 | 5 | drawViewer.install = Vue => { 6 | if (!Vue) { 7 | window.Vue = Vue = _Vue; 8 | } 9 | Vue.component(drawViewer.name, drawViewer) 10 | } 11 | 12 | if (typeof window !== 'undefined' && window.Vue) { 13 | drawViewer.install(window.Vue) 14 | } 15 | export default drawViewer 16 | -------------------------------------------------------------------------------- /src/components/layerManager.vue: -------------------------------------------------------------------------------- 1 | 58 | 238 | 359 | 381 | -------------------------------------------------------------------------------- /src/components/markerViewer.vue: -------------------------------------------------------------------------------- 1 | 125 | 126 | 387 | 388 | 703 | -------------------------------------------------------------------------------- /src/core/Graphic.js: -------------------------------------------------------------------------------- 1 | import GraphicType from "./GraphicType"; 2 | import * as Cesium from 'cesium' 3 | const console = window.console; 4 | import { 5 | CVT 6 | } from '@/js/utils'; 7 | class BaseGraphic { 8 | constructor(viewer) { 9 | if (viewer instanceof Cesium.Viewer === false) { 10 | throw new Error('viewer不是一个有效的Cesium Viewer对象.') 11 | } 12 | this.viewer = viewer 13 | this._type = undefined 14 | this._gvtype = undefined; 15 | this._gvid = undefined; 16 | this._name = undefined; 17 | this._attachment = [] 18 | } 19 | get attachment() { 20 | return this._attachment 21 | } 22 | set attachment(v) { 23 | this._attachment = v 24 | } 25 | get type() { 26 | return this._type; 27 | } 28 | get mtype() { 29 | return this._gvtype 30 | } 31 | set mtype(v) { 32 | this._gvtype = v; 33 | } 34 | get mid() { 35 | return this._gvid; 36 | } 37 | set mid(v) { 38 | this._gvid = v; 39 | if (this.graphic) { 40 | this.graphic.mid = this._gvid 41 | } 42 | } 43 | get mname() { 44 | return this._name 45 | } 46 | set mname(v) { 47 | this._name = v; 48 | if (this.graphic) { 49 | this.graphic.mname = this.mname; 50 | if (this.graphic.label) { 51 | this.graphic.label.text = v; 52 | } 53 | } 54 | } 55 | get show() { 56 | if (this.graphic) { 57 | return this.graphic.show 58 | } 59 | return false 60 | } 61 | set show(v) { 62 | if (this.graphic) { 63 | this.graphic.show = v 64 | } 65 | } 66 | zoomTo() { 67 | if (this.graphic) { 68 | this.viewer.flyTo(this.graphic) 69 | } 70 | } 71 | coordinates() { 72 | if (this.position instanceof Cesium.Cartesian3) { 73 | const coor = CVT.cartesian2Degrees(this.position, this.viewer) 74 | return [coor.lon, coor.lat, coor.height] 75 | } else if (this.positions instanceof Array) { 76 | const pts = [] 77 | for (let p of this.positions) { 78 | const c = CVT.cartesian2Degrees(p, this.viewer) 79 | pts.push([c.lon, c.lat, c.height]) 80 | } 81 | if (this.type === 'POLYLINE') { 82 | return pts 83 | } else { 84 | return [pts] 85 | } 86 | 87 | } 88 | } 89 | toGeoJson() { 90 | const type = { 91 | 'MARKER': 'Point', 92 | 'POLYLINE': 'LineString', 93 | 'POLYGON': 'POLYGON', 94 | 'LABEL': 'Point' 95 | } 96 | return { 97 | "type": "Feature", 98 | "properties": { 99 | name: this.mname, 100 | mtype: this.mtype 101 | }, 102 | "geometry": { 103 | "type": type[this.type], 104 | "coordinates": this.coordinates() 105 | } 106 | } 107 | 108 | } 109 | } 110 | class CesiumBillboard extends BaseGraphic { 111 | /** 112 | * Cesium Marker 113 | * @param {Viewer} viewer Cesium Viewer 114 | * @param {*} options describles a billboard. 115 | * 遵循和Cesium BillboardGraphic相同的方式. 116 | */ 117 | constructor(viewer, options, labelOption = CesiumBillboard.defaultLabelStyle) { 118 | super(viewer); 119 | this.viewer = viewer; 120 | this._type = 'MARKER'; 121 | this.mtype = GraphicType.MARKER; 122 | this.position = options.position; 123 | options.image = options.image || CesiumBillboard.defaultStyle.image; 124 | 125 | labelOption.text = options.label; 126 | this.labelOptions = labelOption; 127 | const self = this; 128 | this.options = { 129 | mname: this._name, 130 | mtype: this.mtype, 131 | mid: this.mid, 132 | position: self.position, 133 | billboard: options, 134 | label: labelOption 135 | } 136 | this.graphic = undefined; 137 | this.name = ''; 138 | this.description = ''; 139 | this.create(); 140 | } 141 | 142 | get text() { 143 | return this.graphic.label.text 144 | } 145 | set text(v) { 146 | this.graphic.label.text = v 147 | this.mname = v 148 | } 149 | 150 | get font() { 151 | return this.graphic.label.font.getValue(this.viewer.clock.currentTime) 152 | } 153 | set font(font) { 154 | this.graphic.label.font = font 155 | } 156 | get color() { 157 | return this.graphic.label.fillColor 158 | } 159 | set color(color) { 160 | this.graphic.label.fillColor = color 161 | } 162 | /** 163 | * 164 | * @param {*} option 定义一个LabelGraphic 165 | */ 166 | setLabel(option) { 167 | if (!this.graphic) { 168 | return 169 | } 170 | const keys = Object.keys(option) 171 | for (let key of keys) { 172 | this.graphic.label[key] = option[key] 173 | } 174 | } 175 | create() { 176 | this.graphic = this.viewer.entities.add(this.options); 177 | } 178 | remove() { 179 | if (this.viewer) { 180 | this.viewer.entities.remove(this.graphic); 181 | } 182 | this.graphic = undefined; 183 | } 184 | updateText(text, description) { 185 | if (this.graphic) { 186 | this.graphic.label.text = text; 187 | this.name = text; 188 | this.description = description; 189 | this.mname = text 190 | } 191 | } 192 | updateImage(img) { 193 | if (this.graphic) { 194 | if (img === undefined) { 195 | this.graphic.label.pixelOffset = undefined 196 | this.graphic.billboard.image = undefined 197 | } else { 198 | this.graphic.label.pixelOffset = this.labelOptions.pixelOffset 199 | this.graphic.billboard.image = img; 200 | } 201 | } 202 | } 203 | updatePosition(position) { 204 | this.position = position; 205 | } 206 | stopEdit() { 207 | if (this.graphic && this.graphic.position) { 208 | this.graphic.position = new Cesium.ConstantProperty(this.position) 209 | } 210 | } 211 | startEdit() { 212 | const self = this; 213 | if (this.graphic) { 214 | this.graphic.position = new Cesium.CallbackProperty(function() { 215 | return self.position; 216 | }, false) 217 | } 218 | } 219 | destroy() { 220 | this.remove(); 221 | this.viewer = undefined; 222 | this.options = undefined; 223 | this.position = undefined; 224 | } 225 | static fromDegrees(viewer, position) { 226 | const option = CesiumBillboard.defaultStyle; 227 | option.position = Cesium.Cartesian3.fromDegrees(position.lon, position.lat, position.height) 228 | return new CesiumBillboard(viewer, option); 229 | } 230 | static fromRadians(viewer, position) { 231 | const option = CesiumBillboard.defaultStyle; 232 | option.position = Cesium.Cartesian3.fromRadians(position.lon, position.lat, position.height) 233 | return new CesiumBillboard(viewer, option); 234 | } 235 | 236 | static defaultStyle = { 237 | image: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ1IDc5LjE2MzQ5OSwgMjAxOC8wOC8xMy0xNjo0MDoyMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkJEN0VBRDA0MzJCRTExRUE5MjY2QTg3OUVFNjUyQzhCIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkJEN0VBRDA1MzJCRTExRUE5MjY2QTg3OUVFNjUyQzhCIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QkQ3RUFEMDIzMkJFMTFFQTkyNjZBODc5RUU2NTJDOEIiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QkQ3RUFEMDMzMkJFMTFFQTkyNjZBODc5RUU2NTJDOEIiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz6qzlgwAAADR0lEQVR42uxXTUhUURQ+88aZ93JSZyiDCCkt+0VLtGzRn/QntotMoqBEol1Ui9JVtNCsoKJNSFgQREjRKiXF/mxToIQK2YBZIRKlaE1pb+Y5M33n+dRJfTN3ZoLZdOG7986dc75z5v6cc8YSDAYpkU2iBLeEO5AUpfxy4BCwHVgLLDTWh4B3wEvgPvBBlNAy1x1w3fP89XnkcOoyDJeBA6wTgZMJHwJnwfNpBk/0DkCpFEM9kKL6iR591qixX6OukQB9+x3QZRbNkyjXJdG+DBvtX2ojxaov/wQqwPUgZgegUIHhFss19o9TVYdK/aOBsD8/wyHRxXwFziRN7sZx8NWbOWB6CSFchKGOjZ9/q9KRtrGIxrmxDMuyjnFcdQaX+B3g7QZ6gCWXur1U2+WN6YZX5sp0Lkfm6YBxaT2iO3CKjbcP+elKtzfmJ8a6zMFcwGnRHVB4J/mJFbeM0ptBf1zvvDDdSk/2OCafagagRtqBvWy8c9gft3FuzMFcRswoFjmC3dy1DIybktqhVVugUF9pCn0EeG4PE1NDuHaJOFDA3auv5r/+Qp5CJ1bZyWW3kBPgedV62VQ+hGujiANZ3Lk95g6UZdlmrR1dYTeVD+HKFHHAyd2w+u/SdAiXS8QBXVpJMg/5DX3arLW7vZqpvGyd4lJFHOCsRuuc5reKo1yd20fffUEdPK/pVE3lc1xTXL0i6fgxsKEs02b6DH2IyJXtqg6RdjDTFsodMRAtBvq8AVJ2NP2i9z8CcZ3/6jSJXpTMJ1nSt58v+JdIR8AC1VCg21uSKdVmidk469aDQ56wUj3TeLhkxHvWCmx7jWMoez5GHi0YtfGGomTanK4XB21GENKEkhHyt2ZUP24maC120Mo08fKRZVnHMO5mLoNTvB6AwiCGnZyWs1MlegbC8mx72HqMvzuGgMSyrIPWzRwGV/RVMRQ5j2/laOpAXLi6SaEmZLb8BdZZsrzG310rVMgxEUOesq7BEXdRynG2Bjij66BrQol2o2eiVji5RqYSlGCW6UB2nesR8PjiLkpnEHCqvmM8VTJ5QeXQbzbRnyPuwoEo4QRuAv7gdPMba85o+WJxYBJ5QDPQYsxj4rH8/3OaaAf+CDAAVvn1VEy/MOwAAAAASUVORK5CYII=', 238 | verticalOrigin: Cesium.VerticalOrigin.BASELINE 239 | } 240 | static defaultLabelStyle = { 241 | font: '28px sans-serif', 242 | fillColor: Cesium.Color.WHITE, 243 | showBackground: true, 244 | style: Cesium.LabelStyle.FILL_AND_OUTLINE, 245 | // outlineWidth: 2, 246 | verticalOrigin: Cesium.VerticalOrigin.BOTTOM, 247 | pixelOffset: new Cesium.Cartesian2(0, 40), 248 | heightReference: Cesium.HeightReference.NONE, 249 | horizontalOrigin: Cesium.HorizontalOrigin.CENTER 250 | } 251 | } 252 | class CesiumPoint extends BaseGraphic { 253 | /** 254 | * Cesium PointGraphic 255 | * @param {Viewer} viewer Cesium.Viewer 256 | * @param {Object} options Describes a point. positions定义它的位置信息 257 | * (positions为数组将同时创建多个Point), 258 | * 属性信息的定义和Cesium.PointGraphics相同 259 | */ 260 | constructor(viewer, options = CesiumPoint.defaultStyle) { 261 | super(viewer); 262 | this._type = 'POINT'; 263 | this.mtype = GraphicType.POINT; 264 | //allow mutiple to be created points at one time 265 | this.positions = options.positions; 266 | //only one point 267 | const self = this; 268 | if (this.positions instanceof Cesium.Cartesian3) { 269 | this.options = { 270 | mname: this._name, 271 | mid: this._gvid, 272 | mtype: this._gvtype, 273 | position: new Cesium.CallbackProperty(function() { 274 | return self.positions; 275 | }, false), 276 | point: options 277 | } 278 | } 279 | //mutiple points 280 | else if (this.positions instanceof Array) { 281 | this.options = []; 282 | for (let i = 0; i < this.positions.length; i++) { 283 | const point = { 284 | mname: this._name, 285 | mid: this._gvid, 286 | mtype: this._gvtype, 287 | position: new Cesium.CallbackProperty(function() { 288 | return self.positions[i]; 289 | }, false), 290 | point: options 291 | } 292 | this.options.push(point); 293 | } 294 | } else { 295 | throw new Error('options参数错误.'); 296 | } 297 | this.graphic = []; 298 | this.create(); 299 | 300 | 301 | } 302 | 303 | create() { 304 | if (this.options instanceof Array) { 305 | this.graphic = this.options.map(_ => { 306 | const entity = this.viewer.entities.add(_); 307 | return entity 308 | }) 309 | } else { 310 | this.graphic = this.viewer.entities.add(this.options); 311 | } 312 | } 313 | remove() { 314 | if (this.graphic instanceof Array) { 315 | this.graphic.map(_ => { 316 | this.viewer.entities.remove(_); 317 | }) 318 | } else { 319 | this.viewer.entities.remove(this.graphic); 320 | } 321 | this.graphic = undefined; 322 | } 323 | startEdit() { 324 | if (this.graphic instanceof Cesium.Entity) { 325 | this.graphic.position = new Cesium.CallbackProperty(() => { 326 | return this.positions; 327 | }, false) 328 | } else if (this.graphic instanceof Array) { 329 | const count = this.graphic.length 330 | for (let i = 0; i < count; i++) { 331 | this.graphic[i].position = new Cesium.CallbackProperty(() => { 332 | return this.positions[i]; 333 | }, false) 334 | } 335 | } 336 | } 337 | /** 338 | * 在确定点的位置之后,将CallBackProperty重置为一个普通对象 339 | * 因为当点足够多时,CallBackProperty会在一定程度上影响系统性能, 340 | * 后面的Polyline,Polygon也是出于同样的考虑 341 | * ps:后来我发现并没有必要,因为Entity会强制转为Property 342 | */ 343 | stopEdit() { 344 | if (this.graphic instanceof Cesium.Entity) { 345 | this.graphic.position = this.graphic.position.getValue(this.viewer.clock.currentTime); 346 | } else if (this.graphic instanceof Array) { 347 | this.graphic = this.graphic.map(_ => { 348 | return _.position = _.position.getValue(this.viewer.clock.currentTime); 349 | }) 350 | } 351 | } 352 | 353 | /** 354 | * 判断两个点是否在同一位置 355 | * @param {*} node1 356 | * @param {*} node2 357 | */ 358 | static equalPosition(node1, node2) { 359 | if (!(node1 instanceof Cesium.Cartesian3 && node2 instanceof Cesium.Cartesian3)) { 360 | throw new Error('node不是一个有效的Cartesian3对象') 361 | } 362 | return (node1.x === node2.x && 363 | node1.y === node2.y && 364 | node1.z === node2.z) 365 | } 366 | static setStyle(node, option) { 367 | const keys = Object.keys(option) 368 | for (let key of keys) { 369 | node.point[key] = option[key] 370 | } 371 | } 372 | contain(node) { 373 | if (this.graphic instanceof Cesium.Cartesian3) { 374 | return this.graphic === node 375 | } else if (this.graphic instanceof Array) { 376 | const count = this.graphic.length 377 | for (let i = 0; i < count; i++) { 378 | if (this.graphic[i] === node) { 379 | return i 380 | } 381 | } 382 | } 383 | return -1 384 | } 385 | static defaultStyle = { 386 | color: Cesium.Color.RED, 387 | pixelSize: 5, 388 | outlineColor: Cesium.Color.WHITE, 389 | outlineWidth: 3, 390 | heightReference: Cesium.HeightReference.CLAMP_TO_GROUND 391 | } 392 | static editStyle = { 393 | color: Cesium.Color.RED, 394 | pixelSize: 5, 395 | outlineColor: Cesium.Color.AQUA, 396 | outlineWidth: 3, 397 | heightReference: Cesium.HeightReference.CLAMP_TO_GROUND 398 | } 399 | static selectedStyle = { 400 | color: Cesium.Color.AQUA, 401 | pixelSize: 5, 402 | outlineColor: Cesium.Color.AQUA, 403 | outlineWidth: 3 404 | } 405 | static fromDegrees(viewer, positions) { 406 | const options = CesiumPoint.defaultStyle; 407 | if (positions instanceof Array) { 408 | options.positions = positions.map(_ => { 409 | if (_.lon === undefined || _.lat === undefined) { 410 | throw new Error('参数错误'); 411 | } 412 | return Cesium.Cartesian3.fromDegrees(_.lon, _.lat, _.height); 413 | }) 414 | } else { 415 | if (positions.lon === undefined || positions.lat === undefined) { 416 | throw new Error('参数错误'); 417 | } 418 | options.positions = Cesium.Cartesian3.fromDegrees( 419 | positions.lon, 420 | positions.lat, 421 | positions.height); 422 | } 423 | return new CesiumPoint(viewer, options); 424 | } 425 | static fromRadians(viewer, positions) { 426 | const options = CesiumPoint.defaultStyle; 427 | if (positions instanceof Array) { 428 | options.positions = positions.map(_ => { 429 | if (_.lon === undefined || _.lat === undefined) { 430 | throw new Error('参数错误'); 431 | } 432 | return Cesium.Cartesian3.fromRadians(_.lon, _.lat, _.height); 433 | }) 434 | } else { 435 | if (positions.lon === undefined || positions.lat === undefined) { 436 | throw new Error('参数错误'); 437 | } 438 | options.positions = Cesium.Cartesian3.fromRadians( 439 | options.positions.lon, 440 | options.positions.lat, 441 | options.positions.height); 442 | } 443 | return new CesiumPoint(viewer, options); 444 | } 445 | static isCesiumPoint(obj) { 446 | if (Cesium.defined(obj) && obj.id && obj.id.mtype === GraphicType.POINT) { 447 | return true 448 | } 449 | return false 450 | } 451 | destroy() { 452 | this.remove() 453 | this.viewer = undefined; 454 | this.options = undefined; 455 | this.graphic = undefined; 456 | } 457 | } 458 | 459 | class CesiumPolyline extends BaseGraphic { 460 | /** 461 | * Cesium PolylineGraphic 462 | * @param {Viewer} viewer Cesium.Viewer 463 | * @param {Object} options Describes a polyline. positions定义它的顶点集合, 464 | * 属性定义遵循和Cesium.PolylineGraphic相同的定义方式。 465 | */ 466 | constructor(viewer, options = CesiumPolyline.defaultStyle) { 467 | super(viewer); 468 | this._type = 'POLYLINE'; 469 | this.mtype = GraphicType.POLYLINE; 470 | this.positions = options.positions || []; 471 | const self = this; 472 | const _update = function() { 473 | return self.positions; 474 | }; 475 | this.options = { 476 | mname: this._name, 477 | mid: this._gvid, 478 | mtype: this._gvtype, 479 | polyline: options, 480 | properties: options.properties 481 | }; 482 | delete options.properties 483 | this.options.polyline.positions = new Cesium.CallbackProperty(_update, false); 484 | this.graphic = undefined; 485 | this.nodeGraphic = undefined; 486 | this.node = false; 487 | this.create(); 488 | } 489 | 490 | get material() { 491 | if (this.graphic) { 492 | return this.graphic.polyline.material //.getValue() 493 | } 494 | return undefined 495 | } 496 | get width() { 497 | if (this.graphic) { 498 | return this.graphic.polyline.width.getValue(this.viewer.clock.currentTime) 499 | } 500 | return undefined 501 | 502 | } 503 | get properties() { 504 | if (this.graphic) { 505 | return this.graphic.properties 506 | } 507 | return false 508 | } 509 | set properties(v) { 510 | if (this.graphic) { 511 | this.graphic.properties = v 512 | } 513 | } 514 | addNode(node) { 515 | if (node instanceof Cesium.Cartesian3) { 516 | this.positions.push(node) 517 | } 518 | } 519 | popNode() { 520 | this.positions.pop() 521 | } 522 | updateNode(index, node) { 523 | if (index < 0 || index > this.positions.length - 1) { 524 | throw new Error('无效的index') 525 | } 526 | if (node instanceof Cesium.Cartesian3 === false) { 527 | throw new Error('无效的node') 528 | } 529 | this.positions[index] = node 530 | } 531 | dropNode(index) { 532 | this.positions.splice(index, 1) 533 | } 534 | create() { 535 | if (this.viewer) { 536 | this.graphic = this.viewer.entities.add(this.options); 537 | // this.graphic.mtype = 'CesiumPolyline' 538 | } 539 | } 540 | /** 541 | * 542 | * @param {Object} options describles a points. 543 | * 遵循和Cesiun.PointGraphic相同的定义方式 544 | */ 545 | createNode(options = CesiumPoint.defaultStyle) { 546 | options.positions = this.positions; 547 | options.clampToGround = this.options.polyline.clampToGround 548 | this.nodeGraphic = new CesiumPoint(this.viewer, options); 549 | this.node = true; 550 | } 551 | /** 552 | * 对于Polyline的编辑,需要做下面几件事 553 | * 1.要素的positions要变成CallbackProperty 554 | * 2.创建要素节点 555 | * 3.要素高亮显示 556 | */ 557 | startEdit() { 558 | if (!Cesium.defined(this.graphic)) { 559 | return; 560 | } 561 | // this.remove() 562 | // const self = this 563 | // const attrs = Object.keys(CesiumPolyline.selectedStyle) 564 | // for (let attr of attrs) { 565 | // this.graphic.polyline[attr] = CesiumPolyline.selectedStyle[attr] 566 | // } 567 | //this.graphic.polyline.material = CesiumPolyline.selectedStyle.material 568 | 569 | this.graphic.polyline.positions = new Cesium.CallbackProperty(() => { 570 | return this.positions 571 | }, false) 572 | if (this.node === false) { 573 | this.createNode() 574 | } 575 | // const evt = new CustomEvent('startEdit', { 576 | // detail: { graphicType: 'POLYLINE' } 577 | // }) 578 | // document.dispatchEvent(evt) 579 | 580 | } 581 | stopEdit() { 582 | if (this.graphic instanceof Cesium.Entity) { 583 | this.graphic.polyline.positions = this.positions; 584 | // this.graphic.polyline.material = this.options.polyline.material 585 | } 586 | this.removeNode() 587 | // const endEvent = new CustomEvent('stopEdit') 588 | // document.dispatchEvent(endEvent) 589 | // window.aa = this.graphic 590 | } 591 | remove() { 592 | if (this.viewer) { 593 | this.viewer.entities.remove(this.graphic); 594 | this.graphic = undefined; 595 | this.removeNode() 596 | } 597 | 598 | } 599 | removeNode() { 600 | if (this.node) { 601 | this.nodeGraphic.remove(); 602 | // this.nodeGraphic = undefined 603 | this.node = false; 604 | } 605 | } 606 | setMaterial(material) { 607 | this.graphic.polyline.material = material 608 | } 609 | 610 | static fromDegrees(viewer, positions, properties = {}) { 611 | positions = positions.map(_ => { 612 | return Cesium.Cartesian3.fromDegrees(_.lon, _.lat, _.height); 613 | }) 614 | const options = { 615 | positions, 616 | ...CesiumPolyline.defaultStyle, 617 | properties 618 | }; 619 | 620 | const pl = new CesiumPolyline(viewer, options); 621 | pl.stopEdit() 622 | return pl; 623 | } 624 | static fromRadians(viewer, positions) { 625 | positions = positions.map(_ => { 626 | return Cesium.Cartesian3.fromRadians(_.lon, _.lat, _.height); 627 | }) 628 | const options = { 629 | positions, 630 | ...CesiumPolyline.defaultStyle 631 | }; 632 | const pl = new CesiumPolyline(viewer, options); 633 | return pl; 634 | } 635 | static defaultStyle = { 636 | clampToGround: true, 637 | material: Cesium.Color.fromCssColorString('rgba(247,224,32,1)'), 638 | width: 3 639 | } 640 | static selectedStyle = { 641 | clampToGround: true, 642 | material: Cesium.Color.AQUA, 643 | width: 3 644 | } 645 | destroy() { 646 | this.remove(); 647 | this.viewer = undefined; 648 | this.options = undefined; 649 | this.positions = undefined; 650 | if (this.nodeGraphic) { 651 | this.nodeGraphic.destroy(); 652 | } 653 | 654 | this.nodeGraphic = undefined; 655 | } 656 | } 657 | class CesiumPolygon extends BaseGraphic { 658 | /** 659 | * Cesium PolygonGraphic 660 | * @param {Viewer} viewer Cesium.Viewer 661 | * @param {Object} options describles a polygon. 662 | * positions定义其位置信息, 属性信息遵循和Cesium.PolygonGraphic相同的定义方式 663 | */ 664 | constructor(viewer, options = CesiumPolygon.defaultStyle) { 665 | super(viewer); 666 | this._type = 'POLYGON'; 667 | this.mtype = GraphicType.POLYGON; 668 | this.positions = options.positions || []; 669 | this.nodePositions = [...this.positions] 670 | 671 | const self = this; 672 | this.options = { 673 | mid: this.mid, 674 | mtype: this.mtype, 675 | polygon: { 676 | hierarchy: new Cesium.CallbackProperty(function() { 677 | return new Cesium.PolygonHierarchy(self.positions) 678 | }, false), 679 | ...options 680 | }, 681 | properties: options.properties 682 | }; 683 | delete options.properties 684 | this.node = false; 685 | this.graphic = undefined; 686 | this.nodeGraphic = undefined; 687 | this.outlineGraphic = undefined; 688 | this.outline = options.outline; 689 | this.create(); 690 | } 691 | get properties() { 692 | if (this.graphic) { 693 | return this.graphic.properties 694 | } 695 | return false 696 | } 697 | set properties(v) { 698 | if (this.graphic) { 699 | this.graphic.properties = v 700 | } 701 | } 702 | get outlineStyle() { 703 | if (this.outlineGraphic) { 704 | return this.outlineGraphic.polyline 705 | } 706 | return undefined 707 | } 708 | set outlineStyle(style) { 709 | const options = {} 710 | if (Cesium.defined(this.outlineGraphic)) { 711 | const pl = this.outlineGraphic.graphic.polyline 712 | options.material = style.outlineColor || pl.material 713 | options.width = style.outlineWidth || pl.width 714 | this.outlineGraphic.graphic.polyline.material = options.material 715 | this.outlineGraphic.graphic.polyline.width = options.width 716 | this.outlineGraphic.options.polyline.material = options.material 717 | this.outlineGraphic.options.polyline.width = options.width 718 | } else { 719 | this.outline && this.createOutline(options) 720 | } 721 | 722 | } 723 | get material() { 724 | if (this.graphic) { 725 | return this.graphic.polygon.material.getValue(this.viewer.clock.currentTime).color 726 | } 727 | return undefined 728 | } 729 | get outlineColor() { 730 | if (this.outlineGraphic) { 731 | return this.outlineGraphic.graphic.polyline.material.getValue(this.viewer.clock.currentTime).color 732 | } 733 | return CesiumPolygon.defaultStyle.outlineColor 734 | } 735 | get outlineWidth() { 736 | if (this.outlineGraphic) { 737 | return this.outlineGraphic.graphic.polyline.width.getValue(this.viewer.clock.currentTime) 738 | } 739 | return CesiumPolygon.defaultStyle.outlineWidth 740 | } 741 | addNode(node) { 742 | if (node instanceof Cesium.Cartesian3) { 743 | this.positions.push(node) 744 | //由于边框实质上是一个首尾相连的Polyline 745 | //因此最后一个点需要永远等于第一个点 746 | const count = this.nodePositions.length 747 | if (count === 0) { 748 | this.nodePositions.push(this.positions[0]) 749 | this.nodePositions.push(this.positions[0]) 750 | } 751 | if (count >= 2) { 752 | // this.nodePositions.insert(count-1,node) 753 | this.nodePositions[count] = this.nodePositions[count - 1] 754 | this.nodePositions[count - 1] = node 755 | } 756 | 757 | } 758 | } 759 | updateNode(index, node) { 760 | if (index < 0 || index > this.positions.length - 1) { 761 | throw new Error('无效的index') 762 | } 763 | if (node instanceof Cesium.Cartesian3 === false) { 764 | throw new Error('无效的node') 765 | } 766 | this.positions[index] = node 767 | if (index === 0) { 768 | this.nodePositions[0] = node 769 | this.nodePositions[this.nodePositions.length - 1] = node 770 | } else { 771 | this.nodePositions[index] = node 772 | } 773 | } 774 | popNode() { 775 | this.positions.pop() 776 | //nodePositions的最后一个节点是倒数第2个点 777 | this.nodePositions.splice(this.nodePositions.length - 2) 778 | this.nodePositions.push(this.positions[0]) 779 | } 780 | 781 | dropNode(index) { 782 | this.positions.splice(index, 1) 783 | this.nodePositions.splice(index, 1) 784 | if (index === 0) { 785 | this.nodePositions[this.nodePositions.length - 1] = this.nodePositions[0] 786 | } 787 | } 788 | create() { 789 | //Not create outline here. 790 | //Create it with createOutline if necessary 791 | 792 | this.options.polygon.outline = false; 793 | if (this.viewer) { 794 | this.graphic = this.viewer.entities.add(this.options); 795 | // this.graphic.mtype = 'CesiumPolygon'; 796 | if (this.outline) { 797 | this.createOutline() 798 | } 799 | } 800 | } 801 | /** 802 | * 803 | * @param {Object} options describles a points. 804 | * 遵循和Cesiun.PointGraphic相同的定义方式 805 | */ 806 | createNode(options = CesiumPoint.defaultStyle) { 807 | this.node = true; 808 | options.positions = this.positions; 809 | options.clampToGround = this.graphic.polygon.perPositionHeight.getValue(this.viewer.clock.currentTime); 810 | 811 | this.nodeGraphic = new CesiumPoint(this.viewer, options); 812 | } 813 | /** 814 | * 创建多边形边框 815 | * 由于Cesium PolygonGraphic的outlineWidth属性无效(只能为1), 816 | * 这里利用Polyline代替多边形的outline 817 | */ 818 | createOutline(options = {}) { 819 | if (this.options.outline === false) { 820 | console.log('如果您想创建多边形边线,请在options中设置outline为true'); 821 | return; 822 | } 823 | options.width = this.options.polygon.outlineWidth || CesiumPolyline.defaultStyle.width; 824 | options.material = this.options.polygon.outlineColor || CesiumPolyline.defaultStyle.material; 825 | // options.show=this.options.outline 826 | options.clampToGround = !this.graphic.polygon.perPositionHeight.getValue(this.viewer.clock.currentTime); 827 | options.positions = this.nodePositions; 828 | this.outlineGraphic = new CesiumPolyline(this.viewer, options); 829 | this.outline = true 830 | } 831 | /** 832 | * 对于Polygon的要素编辑,需要做下面几件事: 833 | * 1.hierarchy变为CallbackProperty 834 | * 2.创建多边形顶点 835 | * 3.要素高亮显示 836 | * 4.如果多边形定义了outline,outline也要高亮,outline的positions要变为CallbackProperty 837 | */ 838 | startEdit() { 839 | const positions = this.positions 840 | // const nodePositions = this.nodePositions 841 | if (this.graphic instanceof Cesium.Entity) { 842 | this.graphic.polygon.hierarchy = new Cesium.CallbackProperty(function() { 843 | return new Cesium.PolygonHierarchy(positions); 844 | }) 845 | //this.graphic.polygon.material = CesiumPolygon.selectedStyle.material; 846 | if (this.outline) { 847 | this.outlineGraphic.startEdit(); 848 | this.nodeGraphic = this.outlineGraphic.nodeGraphic; 849 | this.node = true; 850 | } else { 851 | this.createNode() 852 | } 853 | } 854 | 855 | } 856 | stopEdit() { 857 | if (this.graphic instanceof Cesium.Entity) { 858 | this.graphic.polygon.hierarchy = new Cesium.PolygonHierarchy(this.positions); 859 | //this.graphic.polygon.material = this.options.polygon.material 860 | } 861 | this.removeNode() 862 | if (this.nodeGraphic) { 863 | this.nodeGraphic.stopEdit(); 864 | } 865 | if (this.outlineGraphic) { 866 | this.outlineGraphic.stopEdit(); 867 | } 868 | 869 | } 870 | remove() { 871 | if (this.viewer) { 872 | this.viewer.entities.remove(this.graphic); 873 | this.graphic = undefined; 874 | this.removeNode() 875 | this.removeOutline() 876 | } 877 | } 878 | removeOutline() { 879 | if (this.outline) { 880 | this.outlineGraphic.remove(); 881 | this.outline = false 882 | this.outlineGraphic = undefined 883 | } 884 | } 885 | removeNode() { 886 | if (this.node) { 887 | this.nodeGraphic.remove(); 888 | this.node = false; 889 | // this.nodePositions=[] 890 | } 891 | } 892 | destroy() { 893 | this.remove() 894 | this.viewer = undefined; 895 | this.positions = undefined; 896 | this.options = undefined; 897 | if (this.nodeGraphic) { 898 | this.nodeGraphic.destroy(); 899 | } 900 | if (this.outlineGraphic) { 901 | this.outlineGraphic.destroy(); 902 | } 903 | this.outlineGraphic = undefined; 904 | this.nodeGraphic = undefined; 905 | } 906 | 907 | /** 908 | * 多边形默认样式 909 | */ 910 | static defaultStyle = { 911 | material: new Cesium.Color.fromCssColorString('rgba(247,224,32,0.5)'), 912 | outline: true, 913 | outlineColor: new Cesium.Color.fromCssColorString('rgba(255,247,145,1)'), 914 | outlineWidth: 2, 915 | perPositionHeight: false 916 | // height:0, 917 | // HeightReference: Cesium.HeightReference.CLAMP_TO_GROUND 918 | //material: new Cesium.ColorMaterialProperty(new Cesium.Color(205, 139, 14, 1)), 919 | 920 | } 921 | static selectedStyle = { 922 | // material: new Cesium.ColorMaterialProperty(Cesium.Color.GREEN.withAlpha(0.4)), 923 | //material: new Cesium.ColorMaterialProperty(new Cesium.Color(205, 139, 14, 1)), 924 | material: new Cesium.ColorMaterialProperty(Cesium.Color.AQUA.withAlpha(0.4)), 925 | outlineColor: Cesium.Color.AQUA.withAlpha(0.4) 926 | //heightReference: Cesium.HeightReference.CLAMP_TO_GROUND 927 | } 928 | static fromDegrees(viewer, positions, properties = {}) { 929 | positions = positions.map(_ => { 930 | return Cesium.Cartesian3.fromDegrees(_.lon, _.lat, _.height); 931 | }); 932 | const options = CesiumPolygon.defaultStyle; 933 | options.positions = positions; 934 | options.properties = properties 935 | const pg = new CesiumPolygon(viewer, options); 936 | pg.stopEdit() 937 | return pg 938 | 939 | } 940 | static fromRadians(viewer, positions) { 941 | positions = positions.map(_ => { 942 | return Cesium.Cartesian3.fromRadians(_.lon, _.lat, _.height); 943 | }); 944 | const options = CesiumPolygon.defaultStyle; 945 | options.positions = positions; 946 | return new CesiumPolygon(viewer, options); 947 | 948 | } 949 | } 950 | class CesiumLabel extends BaseGraphic { 951 | /** 952 | * 953 | * @param {Viewer} viewer Cesium Viewer 954 | * @param {Object} options describles a label. 955 | * positions定义其空间位置(如果positions为Cartesian3数组将创建多个label), 956 | * label属性的定义遵循Cesium LabelGraphic的定义方式 957 | */ 958 | constructor(viewer, options = CesiumLabel.defaultStyle) { 959 | super(viewer); 960 | this._type = 'LABEL'; 961 | this._gvtype = GraphicType.LABEL; 962 | this.position = options.position; 963 | 964 | this.options = { 965 | mid: this._gvid, 966 | mtype: this._gvtype, 967 | position: this.position, 968 | label: options 969 | }; 970 | this.graphic = undefined; 971 | this.create(); 972 | 973 | 974 | } 975 | get color() { 976 | if (this.graphic) { 977 | return this.graphic.label.fillColor.getValue(this.viewer.clock.currentTime) 978 | } 979 | return undefined 980 | } 981 | set color(c) { 982 | if (this.graphic) { 983 | this.graphic.label.fillColor = c 984 | } 985 | } 986 | get font() { 987 | if (this.graphic) { 988 | return this.graphic.label.font.getValue(this.viewer.clock.currentTime) 989 | } 990 | return undefined 991 | } 992 | create() { 993 | 994 | this.graphic = this.viewer.entities.add(this.options); 995 | } 996 | startEdit() { 997 | if (this.graphic) { 998 | this.graphic.label.position = new Cesium.CallbackProperty(() => { 999 | return this.position 1000 | }, false) 1001 | } 1002 | } 1003 | stopEdit() { 1004 | if (this.graphic) { 1005 | this.graphic.label.position = this.position 1006 | } 1007 | } 1008 | remove() { 1009 | this.viewer && this.viewer.entities.remove(this.graphic); 1010 | this.graphic = undefined; 1011 | } 1012 | updateText(text) { 1013 | if (this.graphic) { 1014 | this.graphic.label.text = text; 1015 | this.mname = text 1016 | } 1017 | } 1018 | static defaultStyle = { 1019 | color: Cesium.Color.WHITE, 1020 | font: '28px sans-serif', 1021 | showBackground: true 1022 | } 1023 | destroy() { 1024 | this.remove(); 1025 | this.viewer = undefined; 1026 | this.options = undefined; 1027 | this.positions = undefined; 1028 | } 1029 | } 1030 | class CesiumModel extends BaseGraphic { 1031 | constructor(viewer, options) { 1032 | super(viewer); 1033 | this._type = 'MODEL'; 1034 | this._gvtype = GraphicType.MODEL; 1035 | this.position = options.position 1036 | // this.mname = '未命名' 1037 | this.options = { 1038 | mid: this._gvid, 1039 | mtype: this._gvtype, 1040 | position: this.position, 1041 | model: options 1042 | } 1043 | this.create() 1044 | 1045 | } 1046 | create() { 1047 | this.graphic = this.viewer.entities.add(this.options) 1048 | } 1049 | startEdit() { 1050 | if (this.graphic) { 1051 | this.graphic.label.position = new Cesium.CallbackProperty(() => { 1052 | return this.position 1053 | }, false) 1054 | } 1055 | } 1056 | stopEdit() { 1057 | if (this.graphic) { 1058 | this.graphic.label.position = this.position 1059 | } 1060 | } 1061 | remove() { 1062 | if (this.viewer) { 1063 | this.viewer.entities.remove(this.graphic) 1064 | this.graphic = undefined 1065 | } 1066 | } 1067 | destroy() { 1068 | this.remove() 1069 | this.options = undefined 1070 | this.position = undefined 1071 | } 1072 | set uri(uri) { 1073 | if (this.graphic) { 1074 | this.graphic.model.uri = uri 1075 | } 1076 | } 1077 | set color(c) { 1078 | if (this.graphic) { 1079 | this.graphic.model.color = c 1080 | } 1081 | } 1082 | set mode(m) { 1083 | if (this.graphic) { 1084 | this.graphic.model.colorBlendMode = m 1085 | } 1086 | } 1087 | set mixed(v) { 1088 | if (this.graphic) { 1089 | this.graphic.model.colorBlendAmount = v 1090 | } 1091 | } 1092 | static defaultStyle = { 1093 | colorBlendMode: Cesium.ColorBlendMode.HIGHLIGHT, 1094 | color: Cesium.Color.WHITE, 1095 | colorBlendAmount: 0.5, 1096 | minimumPixelSize: 64 1097 | } 1098 | } 1099 | export { 1100 | CesiumPoint, 1101 | CesiumPolyline, 1102 | CesiumPolygon, 1103 | CesiumLabel, 1104 | CesiumBillboard, 1105 | CesiumModel 1106 | } 1107 | export default { 1108 | CesiumPoint, 1109 | CesiumPolyline, 1110 | CesiumPolygon, 1111 | CesiumLabel, 1112 | CesiumBillboard, 1113 | CesiumModel 1114 | } 1115 | -------------------------------------------------------------------------------- /src/core/GraphicManager.js: -------------------------------------------------------------------------------- 1 | import * as Cesium from 'cesium' 2 | import { 3 | CesiumPoint, 4 | CesiumPolyline, 5 | CesiumPolygon 6 | } from './Graphic' 7 | import utils from '@/js/utils' 8 | import { 9 | CVT 10 | } from '@/js/utils' 11 | import GraphicType from './GraphicType' 12 | import { 13 | saveAs 14 | } from 'file-saver' 15 | const console = window.console; 16 | const LEFT_CLICK = Cesium.ScreenSpaceEventType.LEFT_CLICK; 17 | const RIGHT_CLICK = Cesium.ScreenSpaceEventType.RIGHT_CLICK; 18 | const MOUSE_MOVE = Cesium.ScreenSpaceEventType.MOUSE_MOVE; 19 | const MOUSE_DOWN = Cesium.ScreenSpaceEventType.LEFT_DOWN; 20 | const MOUSE_UP = Cesium.ScreenSpaceEventType.LEFT_UP; 21 | class GraphicManager { 22 | /** 23 | * 鼠标交互绘制线和多边形 24 | * @param {Viewer}} viewer Cesium Viewer 25 | * @param {*} options 预留参数,目前不需要关注 26 | */ 27 | constructor(viewer, options = {}) { 28 | if (viewer instanceof Cesium.Viewer === false) { 29 | throw new Error('viewer不是一个有效的Cesium Viewer') 30 | } 31 | 32 | this.viewer = viewer 33 | this.options = options 34 | /*heightReference 定义几何图形的高程基准 35 | *CLAMP_TO_GROUND:依附地形 36 | *CLAMP_TO_MODEL:依附模型 37 | *NONE:空间线 38 | */ 39 | this._heightReference = 'CLAMP_TO_GROUND' 40 | this._material = undefined 41 | this._style = {} 42 | 43 | this.graphicId = undefined 44 | this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.canvas) 45 | this.graphicType = undefined 46 | this.positions = [] 47 | this.tip = new utils.CursorTip('') 48 | this.tip.visible = false 49 | this.mode = 'ready' 50 | this.dragging = false 51 | // this.init() 52 | // this.addEventListener() 53 | //当前正在编辑的graphic 54 | this.editManager = undefined 55 | this.selectedNodeIndex = -1 56 | //Graphic集合 57 | this.manager = new Map() 58 | const self = this 59 | document.onkeydown = function(event) { 60 | 61 | if (self.mode !== 'edit') return; 62 | 63 | const e = event || window.event || arguments.callee.caller.arguments[0]; 64 | 65 | if (e && e.keyCode == 46) { // 按 delete 66 | if (self.selectedNodeIndex > -1 && self.editManager) { 67 | self.editManager.dropNode(self.selectedNodeIndex) 68 | self.highlightedNode(undefined, self.editManager.nodeGraphic) 69 | self.selectedNodeIndex = -1 70 | } else if (self.editManager) { 71 | self.editManager.destroy() 72 | self.manager.delete(self.editManager.id) 73 | self.mode = 'end' 74 | 75 | self.tip.visible = false 76 | const evt = new CustomEvent('deleteEvent', { 77 | detail: { 78 | mid: self.editManager ? self.editManager.mid : undefined 79 | } 80 | }) 81 | document.dispatchEvent(evt) 82 | self.editManager = undefined 83 | // self.removeEventListener(); 84 | } 85 | 86 | 87 | } 88 | 89 | }; 90 | // this.tip.style.display='none' 91 | 92 | } 93 | get heightReference() { 94 | return this._heightReference; 95 | } 96 | set heightReference(h) { 97 | this._heightReference = h 98 | if (this.editManager) { 99 | this.editManager.heightReference = h 100 | if (this.editManager.type === 'POLYLINE') { 101 | this.editManager.graphic.polyline.clampToGround = /.*GROUND.*/.test(h); 102 | this.editManager.options.polyline.clampToGround = /.*GROUND.*/.test(h); 103 | 104 | } else if (this.editManager.type === 'POLYGON') { 105 | const graphic = this.editManager.graphic; 106 | const options = this.editManager.options; 107 | if (/.*GROUND.*/.test(h)) { 108 | graphic.polygon.perPositionHeight = false; 109 | if (this.editManager.outline) { 110 | this.editManager.outlineGraphic.graphic.polyline.clampToGround = true 111 | } // polygon.heightReference = Cesium.HeightReference.CLAMP_TO_GROUND 112 | // options.polygon.heightReference= Cesium.HeightReference.CLAMP_TO_GROUND 113 | options.polygon.perPositionHeight = false; 114 | } else { 115 | graphic.polygon.perPositionHeight = true; 116 | if (this.editManager.outline) { 117 | this.editManager.outlineGraphic.graphic.polyline.clampToGround = false 118 | } 119 | // polygon.heightReference = Cesium.HeightReference.RELATIVE_TO_GROUND 120 | // options.polygon.heightReference= Cesium.HeightReference.RELATIVE_TO_GROUND 121 | options.polygon.perPositionHeight = true 122 | 123 | } 124 | 125 | } 126 | } 127 | } 128 | get material() { 129 | return this._material; 130 | } 131 | set material(v) { 132 | this._material = v; 133 | if (this.editManager) { 134 | if (this.editManager.type === 'POLYLINE') { 135 | this.editManager.graphic.polyline.material = this._material; 136 | this.editManager.options.polyline.material = this._material; 137 | 138 | } else if (this.editManager.type === 'POLYGON') { 139 | this.editManager.graphic.polygon.material = this._material; 140 | this.editManager.options.polygon.material = this._material; 141 | } 142 | } 143 | } 144 | get style() { 145 | return this._style; 146 | } 147 | set style(option) { 148 | this._style = option; 149 | if (!this.editManager) { 150 | return 151 | } 152 | const keys = Object.keys(option); 153 | for (let key of keys) { 154 | if (this.editManager.type === 'POLYLINE') { 155 | this.editManager.graphic.polyline[key] = option[key]; 156 | this.editManager.options.polyline[key] = option[key]; 157 | } else if (this.editManager.type === 'POLYGON') { 158 | if (key !== 'outline') { 159 | this.editManager.graphic.polygon[key] = option[key]; 160 | } 161 | 162 | this.editManager.options.polygon[key] = option[key]; 163 | } 164 | } 165 | if (this.editManager.type === 'POLYGON') { 166 | this.editManager.outlineStyle = option 167 | } 168 | } 169 | 170 | 171 | 172 | /** 173 | * 174 | * @param {Object} options 定义一个CesiumPolyline 175 | */ 176 | createPolyline(options = CesiumPolyline.defaultStyle) { 177 | this.graphicType = GraphicType.POLYLINE; 178 | const id = this.generateId(); 179 | options.positions = this.positions; 180 | if (/.*GROUND.*/.test(this._heightReference)) { 181 | options.clampToGround = true 182 | } else { 183 | options.clampToGround = false 184 | } 185 | options.material = this.material || options.material 186 | options.width = this.style.width || options.width 187 | const manager = new CesiumPolyline(this.viewer, options); 188 | this.tip.updateText('左键标绘,右键结束.'); 189 | this.tip.visible = true; 190 | manager.mid = id 191 | // manager.id = id 192 | // manager.mname = '未命名'; 193 | manager.heightReference = this.heightReference 194 | this.manager.set(id, manager); 195 | this.graphicId = id 196 | this.editManager = manager 197 | const evt = new CustomEvent('addEvent', { 198 | detail: { 199 | mid: manager.mid, 200 | mtype: manager.mtype, 201 | mname: manager.mname, 202 | } 203 | }) 204 | document.dispatchEvent(evt); 205 | const self = this; 206 | this.handler.setInputAction(e => { 207 | self.tip && self.tip.updatePosition(e.endPosition); 208 | }, MOUSE_MOVE) 209 | this.addEventListener() 210 | return manager 211 | 212 | } 213 | 214 | /** 215 | * 216 | * @param {Object} options 定义一个CesiumPolygon 217 | */ 218 | createPolygon(options = CesiumPolygon.defaultStyle) { 219 | this.graphicType = GraphicType.POLYGON; 220 | const id = this.generateId(); 221 | this.graphicId = id; 222 | options.positions = this.positions; 223 | if (/.*GROUND.*/.test(this._heightReference)) { 224 | options.perPositionHeight = false 225 | // options.heightReference = Cesium.HeightReference.CLAMP_TO_GROUND; 226 | } else { 227 | options.perPositionHeight = true; 228 | // options.heightReference = Cesium.HeightReference.RELATIVE_TO_GROUND; 229 | // options.height = 0 230 | } 231 | 232 | options.material = this.material || options.material; 233 | options.outlineWidth = this.style.outlineWidth || options.outlineWidth; 234 | options.outlineColor = this.style.outlineColor || options.outlineColor; 235 | const manager = new CesiumPolygon(this.viewer, options); 236 | manager.mid = id; 237 | // manager.id = id; 238 | // manager.mname = '未命名'; 239 | manager.heightReference = this.heightReference; 240 | this.tip.visible = true; 241 | this.tip.updateText('左键标绘,右键结束.'); 242 | this.manager.set(id, manager); 243 | this.editManager = manager; 244 | const evt = new CustomEvent('addEvent', { 245 | detail: { 246 | mid: manager.mid, 247 | mtype: manager.mtype, 248 | mname: manager.mname, 249 | } 250 | }) 251 | document.dispatchEvent(evt) 252 | const self = this; 253 | this.handler.setInputAction(e => { 254 | self.tip && self.tip.updatePosition(e.endPosition); 255 | }, MOUSE_MOVE) 256 | this.addEventListener() 257 | return manager; 258 | 259 | } 260 | generateId() { 261 | return (Math.random() * 10000000).toString(16).substr(0, 4) + '-' + (new Date()).getTime() + '-' + Math.random().toString().substr(2, 5); 262 | } 263 | isKnownGraphic(pickedObj) { 264 | if (Cesium.defined(pickedObj) && 265 | pickedObj.id instanceof Cesium.Entity && 266 | (pickedObj.id.mtype === GraphicType.POLYLINE || 267 | pickedObj.id.mtype === GraphicType.POLYGON || 268 | pickedObj.id.mtype === GraphicType.POINT)) { 269 | return true 270 | } 271 | return false 272 | } 273 | /** 274 | * 将当前选中的点设为高亮 275 | * @param {Cartesian3} node 276 | * @param {CesiumPoint} cp 277 | */ 278 | highlightedNode(node, cp) { 279 | const soption = CesiumPoint.selectedStyle 280 | const doption = CesiumPoint.defaultStyle 281 | for (let n of cp.graphic) { 282 | if (n === node) { 283 | CesiumPoint.setStyle(n, soption) 284 | } else { 285 | CesiumPoint.setStyle(n, doption) 286 | } 287 | } 288 | 289 | } 290 | addEventListener() { 291 | const self = this 292 | const viewer = this.viewer 293 | const clickHandler = function(e) { 294 | //编辑要素 295 | if (self.mode === 'edit') { 296 | if (!self.editManager) { 297 | self.removeEventListener(); 298 | return 299 | } 300 | const nodeGraphic = self.editManager.nodeGraphic || 301 | self.editManager.outlineGraphic.nodeGraphic 302 | const pickedObjs = viewer.scene.drillPick(e.position) 303 | let known = false, 304 | pickedObj = undefined 305 | for (let obj of pickedObjs) { 306 | known = self.isKnownGraphic(obj) 307 | if (known && obj.id.mtype === GraphicType.POINT) { 308 | pickedObj = obj 309 | //再事件监听之前移除上次的监听 310 | self.handler.removeInputAction(MOUSE_DOWN); 311 | self.handler.removeInputAction(MOUSE_MOVE); 312 | self.handler.setInputAction(mouseDownHandler, MOUSE_DOWN); 313 | self.handler.setInputAction(moseMoveHandler, MOUSE_MOVE); 314 | break 315 | } 316 | } 317 | // const pickedPosition=CVT.pixel2Cartesian(e.position,viewer) 318 | 319 | if (pickedObj && known) { 320 | if (pickedObj.id.mtype === GraphicType.POINT) { 321 | self.selectedNodeIndex = nodeGraphic.contain(pickedObj.id) 322 | if (self.selectedNodeIndex !== -1) { 323 | self.highlightedNode(pickedObj.id, nodeGraphic) 324 | 325 | } 326 | } else { 327 | self.highlightedNode(pickedObj.id, self.editManager.nodeGraphic) 328 | self.selectedNodeIndex = -1 329 | } 330 | 331 | } else { 332 | self.editManager && self.editManager.stopEdit() 333 | // self.handler.removeInputAction(MOUSE_MOVE); 334 | self.removeEventListener() 335 | self.mode = 'end' 336 | self.selectedNodeIndex = -1 337 | self.editManager = undefined 338 | self.tip.visible = false; 339 | const evt = new CustomEvent('stopEdit') 340 | document.dispatchEvent(evt) 341 | } 342 | return 343 | } 344 | //非法的要素类型 345 | if (self.graphicType != GraphicType.POLYLINE && 346 | self.graphicType != GraphicType.POLYGON) { 347 | return; 348 | } 349 | let cartesian = CVT.pixel2Cartesian(e.position, self.viewer); 350 | if (/.*MODEL.*/.test(self._heightReference)) { 351 | if (!viewer.scene.pickPositionSupported) { 352 | console.log('This browser does not support pickPosition.') 353 | return 354 | } 355 | cartesian = viewer.scene.pickPosition(e.position) 356 | } 357 | //添加第一个点后再监听鼠标移动事件,绘绘完成后移除监听,以免不必要的事件监听 358 | const target = self.manager.get(self.graphicId); 359 | if (target && target.positions.length === 0) { 360 | self.handler.removeInputAction(MOUSE_MOVE); 361 | self.handler.setInputAction(moseMoveHandler, MOUSE_MOVE); 362 | } 363 | if (Cesium.defined(cartesian) && self.manager.has(self.graphicId) && target) { 364 | target.addNode(cartesian); 365 | } 366 | self.mode = 'create' 367 | } 368 | const rightHandler = function() { 369 | const manager = self.manager.get(self.graphicId); 370 | if ((self.mode === 'create') && manager) { 371 | manager.stopEdit(); 372 | self.graphicType = undefined; 373 | self.graphicId = undefined; 374 | self.positions = []; 375 | self.mode = 'end' 376 | self.tip.visible = false 377 | self.editManager = undefined 378 | const evt = new CustomEvent('stopEdit') 379 | document.dispatchEvent(evt) 380 | } else if (self.mode === 'ready') { 381 | self.cancel() 382 | } else if (self.mode === 'edit') { 383 | self.editManager && self.editManager.stopEdit() 384 | // self.handler.removeInputAction(MOUSE_MOVE); 385 | self.removeEventListener() 386 | self.mode = 'end' 387 | self.selectedNodeIndex = -1 388 | self.editManager = undefined 389 | self.tip.visible = false; 390 | } 391 | // self.handler.removeInputAction(MOUSE_MOVE); 392 | const evt = new CustomEvent('stopEdit') 393 | document.dispatchEvent(evt) 394 | self.removeEventListener() 395 | } 396 | 397 | const moseMoveHandler = function(e) { 398 | let cartesian = CVT.pixel2Cartesian(e.endPosition, self.viewer); 399 | if (/.*MODEL.*/.test(self._heightReference)) { 400 | if (!viewer.scene.pickPositionSupported) { 401 | console.log('This browser does not support pickPosition.') 402 | return 403 | } 404 | cartesian = viewer.scene.pickPosition(e.endPosition) 405 | } 406 | if (!Cesium.defined(cartesian)) { 407 | return 408 | } 409 | self.tip.updatePosition(e.endPosition); 410 | if (self.mode === 'create') { 411 | //如果当前是create模式,创建辅助线 412 | if (self.positions.length > 1) { 413 | self.manager.get(self.graphicId).popNode(); 414 | } 415 | //添加临时节点 416 | //再添加第一个节点前,不拾取鼠标移动的坐标 417 | if (self.positions.length > 0) { 418 | // self.positions.push(cartesian); 419 | self.manager.get(self.graphicId).addNode(cartesian); 420 | } 421 | } else if (self.mode == 'edit' && self.dragging) { 422 | if (self.selectedNodeIndex !== -1) { 423 | self.editManager.updateNode(self.selectedNodeIndex, cartesian) 424 | } 425 | 426 | } 427 | } 428 | const mouseDownHandler = function(e) { 429 | self.handler.setInputAction(mouseUpHandler, MOUSE_UP) 430 | const objs = viewer.scene.drillPick(e.position); 431 | let isCesiumPoint = false; 432 | for (let obj of objs) { 433 | if (CesiumPoint.isCesiumPoint(obj)) { 434 | isCesiumPoint = true; 435 | } 436 | 437 | } 438 | if (isCesiumPoint == false) { 439 | return; 440 | } 441 | if (self.mode === 'edit' && self.selectedNodeIndex != -1) { 442 | self.dragging = true 443 | viewer.scene.screenSpaceCameraController.enableRotate = false 444 | 445 | } 446 | 447 | } 448 | const mouseUpHandler = function() { 449 | self.dragging = false; 450 | viewer.scene.screenSpaceCameraController.enableRotate = true; 451 | self.handler.removeInputAction(MOUSE_UP); 452 | // self.handler.removeInputAction(MOUSE_DOWN); 453 | 454 | 455 | } 456 | this.handler.setInputAction(clickHandler, LEFT_CLICK); 457 | this.handler.setInputAction(rightHandler, RIGHT_CLICK); 458 | } 459 | rename(id, name) { 460 | const graphic = this.manager.get(id); 461 | if (Cesium.defined(graphic)) { 462 | graphic.mname = name 463 | } 464 | } 465 | has(id) { 466 | if (this.manager) { 467 | return this.manager.has(id) 468 | } 469 | return false 470 | } 471 | get(id) { 472 | if (this.has(id)) { 473 | return this.manager.get(id) 474 | } 475 | } 476 | /** 477 | * 当图形处于ready状态时,不想画了 478 | */ 479 | cancel() { 480 | 481 | const manager = this.manager.get(this.graphicId); 482 | manager && manager.stopEdit(); 483 | manager && manager.destroy() 484 | this.graphicType = undefined; 485 | this.graphicId = undefined; 486 | this.positions = []; 487 | this.mode = 'end' 488 | this.tip.visible = false 489 | this.editManager = undefined 490 | } 491 | select(type, id, status) { 492 | if (Cesium.defined(id)) { 493 | const manager = this.manager.get(id) 494 | if (manager) { 495 | manager.show = status 496 | } 497 | } 498 | if (Cesium.defined(type)) { 499 | const values = this.manager.values() 500 | for (let v of values) { 501 | if (v.mtype === type) { 502 | v.show = status 503 | } 504 | } 505 | } 506 | } 507 | edit(id) { 508 | const self = this 509 | const manager = self.manager.get(id); 510 | this.handler.setInputAction(e => { 511 | self.tip.updatePosition(e.endPosition); 512 | }, MOUSE_MOVE); 513 | self.graphicId = id; 514 | if (Cesium.defined(manager)) { 515 | // manager.zoomTo() 516 | self.mode = 'edit' 517 | manager.startEdit(); 518 | self.tip.visible = true; 519 | self.tip.updateText('拖动节点编辑,按del删除.') 520 | self.editManager = manager; 521 | const evt = new CustomEvent('startEdit', { 522 | detail: { 523 | graphicType: self.editManager.type, 524 | material: self.editManager.material, 525 | width: self.editManager.width, 526 | outline: self.editManager.outline, 527 | outlineColor: self.editManager.outlineColor, 528 | outlineWidth: self.editManager.outlineWidth, 529 | heightReference: self.editManager.heightReference 530 | } 531 | }) 532 | document.dispatchEvent(evt) 533 | self.addEventListener() 534 | } 535 | } 536 | export (type) { 537 | const json = { 538 | type: "FeatureCollection", 539 | name: "graphic", 540 | crs: { 541 | type: "name", 542 | properties: { 543 | name: "urn:ogc:def:crs:OGC:1.3:CRS84" 544 | } 545 | }, 546 | features: [] 547 | }; 548 | const managers = this.manager.values() 549 | for (let m of managers) { 550 | if (m.type === type) { 551 | json.features.push(m.toGeoJson()) 552 | } 553 | } 554 | const blob = new Blob([JSON.stringify(json)], { 555 | type: "" 556 | }); 557 | saveAs(blob, type + parseInt(Cesium.getTimestamp()) + '.geojson'); 558 | } 559 | import(feat) { 560 | const id = this.generateId(); 561 | let graphic, coordinates, positions = []; 562 | if (feat.geometry.type.toUpperCase() === 'LineString'.toUpperCase()) { 563 | coordinates = feat.geometry.coordinates 564 | for (let c of coordinates) { 565 | positions.push({ 566 | lon: c[0], 567 | lat: c[1], 568 | height: c[2] 569 | }) 570 | } 571 | try { 572 | graphic = CesiumPolyline.fromDegrees(this.viewer, positions, feat.properties); 573 | } catch (e) { 574 | console.log(e) 575 | } 576 | 577 | } else if (feat.geometry.type.toUpperCase() === "POLYGON") { 578 | coordinates = feat.geometry.coordinates[0] 579 | for (let c of coordinates) { 580 | positions.push({ 581 | lon: c[0], 582 | lat: c[1], 583 | height: c[2] 584 | }) 585 | } 586 | graphic = CesiumPolygon.fromDegrees(this.viewer, positions, feat.properties); 587 | } else { 588 | throw new Error('不能识别的数据源.') 589 | } 590 | if (graphic) { 591 | graphic.mid = id; 592 | graphic.mname = feat.properties.name 593 | this.manager.set(id, graphic) 594 | const evt = new CustomEvent('addEvent', { 595 | detail: { 596 | mid: graphic.mid, 597 | mtype: graphic.mtype, 598 | mname: graphic.mname || '未命名', 599 | } 600 | }) 601 | document.dispatchEvent(evt) 602 | } 603 | return graphic 604 | 605 | } 606 | 607 | removeEventListener() { 608 | this.handler.removeInputAction(LEFT_CLICK); 609 | this.handler.removeInputAction(MOUSE_MOVE); 610 | this.handler.removeInputAction(RIGHT_CLICK); 611 | this.handler.removeInputAction(MOUSE_DOWN) 612 | this.handler.removeInputAction(MOUSE_UP) 613 | } 614 | removeAll() { 615 | const values = this.manager.values(); 616 | for (let v of values) { 617 | v.remove(); 618 | v.destroy(); 619 | } 620 | this.manager.clear(); 621 | this.tip.visible = false; 622 | } 623 | 624 | destroy() { 625 | this.activeManager = undefined 626 | this.manager = undefined 627 | this.editManager = undefined 628 | this.removeEventListener() 629 | if (!this.handler.isDestroyed) { 630 | this.handler.destroy() 631 | this.handler = undefined 632 | } 633 | } 634 | destroyManager() { 635 | const manager = this.editManager; 636 | const evt = new CustomEvent('destroyEvent', { 637 | detail: { 638 | mid: manager ? manager.mid : undefined 639 | } 640 | }) 641 | if (manager) { 642 | if (this.mode === 'edit') { 643 | manager && manager.stopEdit(); 644 | } else { 645 | manager && manager.destroy(); 646 | this.manager.delete(this.graphicId) 647 | } 648 | this.editManager = undefined; 649 | } 650 | 651 | this.graphicId = undefined; 652 | this.handler.removeInputAction(MOUSE_MOVE); 653 | this.positions = []; 654 | 655 | document.dispatchEvent(evt); 656 | } 657 | } 658 | export default GraphicManager 659 | -------------------------------------------------------------------------------- /src/core/GraphicType.js: -------------------------------------------------------------------------------- 1 | const GraphicType = (function() { 2 | function _() { 3 | 4 | } 5 | _.MARKER = 0 6 | _.POINT = 5 7 | _.POLYLINE = 1 8 | _.POLYGON = 2 9 | _.LABEL = 3 10 | _.MODEL = 4 11 | return _ 12 | })() 13 | export default GraphicType 14 | -------------------------------------------------------------------------------- /src/core/MarkerManager.js: -------------------------------------------------------------------------------- 1 | import * as Cesium from 'cesium' 2 | import { 3 | CesiumBillboard, 4 | CesiumLabel, 5 | CesiumModel 6 | } from "../core/Graphic"; 7 | import utils from "@/js/utils"; 8 | import GraphicType from "../core/GraphicType"; 9 | import $ from "jquery"; 10 | const cvt = utils.CVT; 11 | const LEFT_CLICK = Cesium.ScreenSpaceEventType.LEFT_CLICK; 12 | const RIGHT_CLICK = Cesium.ScreenSpaceEventType.RIGHT_CLICK; 13 | const MOUSE_MOVE = Cesium.ScreenSpaceEventType.MOUSE_MOVE; 14 | 15 | function setString(str, len) { 16 | var strlen = 0; 17 | var s = ""; 18 | for (var i = 0; i < str.length; i++) { 19 | if (str.charCodeAt(i) > 128) { 20 | strlen += 2; 21 | } else { 22 | strlen++; 23 | } 24 | s += str.charAt(i); 25 | if (strlen >= len) { 26 | return s + "..."; 27 | } 28 | } 29 | return s; 30 | } 31 | export default class MarkerManager { 32 | constructor(viewer, markerOptions = CesiumBillboard.defaultStyle, 33 | labelOptions = CesiumBillboard.defaultLabelStyle, modelOptions = CesiumModel.defaultStyle) { 34 | if (viewer instanceof Cesium.Viewer) { 35 | this._viewer = viewer 36 | } 37 | if (!Cesium.defined(this._viewer)) { 38 | return 39 | } 40 | /** 41 | * 表示当前添加的标记类型,marker,label,model 42 | */ 43 | this.markName = '' 44 | this.markRemark = '' 45 | this.markMode = 'marker' 46 | this.defaultImage = CesiumBillboard.defaultStyle.image 47 | this.selectedImage = undefined 48 | this.popWinPosition = undefined 49 | this.markerid = undefined 50 | this.markerOptions = markerOptions; 51 | this.labelOptions = labelOptions; 52 | this.modelOptions = modelOptions; 53 | this.cursorTip = new utils.CursorTip( 54 | "左键标绘,右键结束.", 55 | "marker-tip", 56 | viewer 57 | ); 58 | this.cursorTip.visible = false; 59 | 60 | this.handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas); 61 | this.manager = new Map(); 62 | this.pickHandler = new Cesium.ScreenSpaceEventHandler(viewer.canvas); 63 | this.init(this._viewer) 64 | 65 | } 66 | init(viewer) { 67 | if (viewer instanceof Cesium.Viewer === false) { 68 | throw new Error("viewer不是一个有效的Cesium Viewer对象."); 69 | } 70 | this.viewer = viewer; 71 | const handler = this.handler; 72 | const self = this; 73 | const manager = this.manager 74 | //气泡跟随地球移动 75 | viewer.scene.postRender.addEventListener(function() { 76 | if (Cesium.defined(self.popWinPosition)) { 77 | const pos = cvt.cartesian2Pixel(self.popWinPosition, viewer); 78 | 79 | const ele = document.getElementById("popContainer"); 80 | if (!ele) { 81 | return; 82 | } 83 | ele.style.left = pos.x - 94 + "px"; 84 | ele.style.top = pos.y - 110 + "px"; 85 | 86 | const curPos = self.popWinPosition; 87 | //标记转到地球背面隐藏气泡 88 | if (utils.pointVisibilityOnEarth(curPos, self._viewer)) { 89 | ele.style.display = "block"; 90 | } else { 91 | ele.style.display = "none"; 92 | } 93 | 94 | // ele.style.display = "block"; 95 | } 96 | }); 97 | const showTip = function(e) { 98 | const obj = viewer.scene.pick(e.position); 99 | if ( 100 | Cesium.defined(obj) && 101 | obj.id instanceof Cesium.Entity && 102 | obj.id.mtype === GraphicType.MARKER 103 | ) { 104 | // self.popWinPosition = cvt.pixel2Cartesian(e.position, viewer); 105 | self.selectedMarker = manager.get(obj.id.mid); 106 | if (self.popDiv) { 107 | self.destroyPopPanle(); 108 | } else { 109 | self.createPopPanel(); 110 | } 111 | } else { 112 | self.destroyPopPanle(); 113 | } 114 | }; 115 | 116 | handler.setInputAction(showTip, LEFT_CLICK); 117 | } 118 | /** 119 | * 开始拾取marker,调用该方法后开始监听鼠标单击,添加标记 120 | * @param {string} type表示何种标记,marker:billboard,label:label,model:model 121 | * @param {string} mode如果mode不是single,将连续添加标记 122 | */ 123 | pick(type = "marker", mode = "single") { 124 | this.markMode = type; 125 | const viewer = this._viewer; 126 | this.cursorTip.visible = true; 127 | const handler = this.pickHandler 128 | const self = this; 129 | const id = this.generateId(); 130 | self.markerid = id; 131 | const manager = this.manager 132 | 133 | const pick = function(e) { 134 | const cartesian = cvt.pixel2Cartesian(e.position, viewer); 135 | if (Cesium.defined(cartesian)) { 136 | // mp.position = cartesian; 137 | let marker; 138 | if (type === "marker") { 139 | marker = self.createMarker(cartesian); 140 | } else if (type === "label") { 141 | marker = self.createLabel(cartesian); 142 | } else if (type === "model") { 143 | marker = self.createModel(cartesian); 144 | } else { 145 | //默认marker 146 | marker = self.createMarker(cartesian); 147 | } 148 | self.visible = true; 149 | manager.set(id, marker); 150 | marker.mid = id; 151 | // marker.mname = "未命名" + viewer.entities.values.length; 152 | 153 | self.selectedMarker = marker; 154 | self.activeMarker = marker; 155 | 156 | self.cursorTip.visible = false; 157 | if (type === "model") { 158 | self.activeMarker = undefined; 159 | } 160 | if (mode === "single") { 161 | handler.removeInputAction(LEFT_CLICK); 162 | handler.removeInputAction(RIGHT_CLICK); 163 | } 164 | const evt = new CustomEvent('marker-add', { 165 | detail: { 166 | id: marker.mid, 167 | name: marker.mname || '未命名', 168 | description: marker.description, 169 | type: marker.mtype, 170 | position: cvt.toDegrees(cartesian, self._viewer) 171 | } 172 | }) 173 | window.dispatchEvent(evt) 174 | marker = undefined 175 | } 176 | }; 177 | const cancel = function() { 178 | handler.removeInputAction(LEFT_CLICK); 179 | handler.removeInputAction(RIGHT_CLICK); 180 | // handler.destroy(); 181 | self.cursorTip.visible = false; 182 | const id = self.activeMarker ? self.activeMarker.id : undefined 183 | const evt = new CustomEvent('marker-delete', { 184 | detail: { 185 | id: id 186 | } 187 | }) 188 | window.dispatchEvent(evt) 189 | 190 | self.activeMarker = undefined; 191 | //handler=undefined 192 | }; 193 | const updateTip = function(e) { 194 | self.cursorTip.updatePosition(e.endPosition); 195 | }; 196 | handler.setInputAction(cancel, RIGHT_CLICK); 197 | 198 | handler.setInputAction(pick, LEFT_CLICK); 199 | handler.setInputAction(updateTip, MOUSE_MOVE); 200 | } 201 | get(id) { 202 | if (this.has(id)) { 203 | return this.manager.get(id) 204 | } 205 | } 206 | has(id) { 207 | if (this.manager) { 208 | return this.manager.has(id) 209 | } 210 | return false 211 | } 212 | createMarker(cartesian) { 213 | 214 | const mp = this.labelOptions; 215 | const marker = new CesiumBillboard( 216 | this._viewer, { 217 | ...this.markerOptions, 218 | position: cartesian, 219 | image: this.selectedImage 220 | }, 221 | mp 222 | ); 223 | return marker; 224 | } 225 | changeHandler(img) { 226 | this.selectedImage = img; 227 | this.activeMarker.updateImage(this.selectedImage); 228 | } 229 | panelPosition() { 230 | if (this.activeMarker) { 231 | if (this.markMode === "marker") { 232 | const position = this.activeMarker.graphic.position.getValue(this.viewer.clock.currentTime); 233 | const pixel = cvt.cartesian2Pixel(position, this._viewer); 234 | const x = pixel.x > 170 ? pixel.x - 170 : pixel.x + 10; 235 | const y = pixel.y > 210 ? pixel.y - 240 : pixel.y + 50; 236 | return { 237 | x: x, 238 | y: y 239 | }; 240 | } else { 241 | const position = this.activeMarker.graphic.position.getValue(this.viewer.clock.currentTime); 242 | const pixel = cvt.cartesian2Pixel(position, this._viewer); 243 | const x = pixel.x + 10; 244 | const y = pixel.y - 25; 245 | return { 246 | x: x, 247 | y: y 248 | }; 249 | } 250 | } else { 251 | return { 252 | x: 0, 253 | y: 0 254 | }; 255 | } 256 | } 257 | createLabel(cartesian) { 258 | const options = this.labelOptions; 259 | options.position = cartesian; 260 | 261 | const marker = new CesiumLabel(this._viewer, options); 262 | return marker; 263 | } 264 | createModel(cartesian) { 265 | const options = this.modelOptions; 266 | options.position = cartesian; 267 | const marker = new CesiumModel(this._viewer, options); 268 | 269 | return marker; 270 | } 271 | removeEventListener() { 272 | const pickHandler = this.pickHandler 273 | if (pickHandler) { 274 | if (!pickHandler.isDestroyed()) { 275 | // pickHandler.destroy(); 276 | pickHandler.removeInputAction(LEFT_CLICK); 277 | pickHandler.removeInputAction(RIGHT_CLICK); 278 | pickHandler.removeInputAction(MOUSE_MOVE); 279 | } 280 | } 281 | } 282 | stopPick() { 283 | this.removeEventListener(); 284 | if (this.activeMarker) { 285 | this.activeMarker.destroy(); 286 | const evt = new CustomEvent('marker-delete', { 287 | detail: { 288 | id: this.activeMarker.mid 289 | } 290 | }) 291 | window.dispatchEvent(evt) 292 | 293 | } 294 | this.activeMarker = undefined; 295 | this.cursorTip.visible = false; 296 | } 297 | zoomTo(id) { 298 | if (this.manager.has(id)) { 299 | this.manager.get(id).zoomTo(); 300 | } 301 | } 302 | edit(id) { 303 | const manager = this.manager 304 | if (manager.has(id)) { 305 | const mm = manager.get(id); 306 | this.activeMarker = mm 307 | mm.startEdit() 308 | if ( 309 | mm.mtype === GraphicType.MARKER || 310 | mm.mtype === GraphicType.LABEL 311 | ) { 312 | this.markName = this.activeMarker.mname; 313 | this.markRemark = this.activeMarker.description; 314 | this.visible = true; 315 | } 316 | // this.activeMarker.zoomTo(); 317 | const pixel = this.panelPosition() 318 | const evt = new CustomEvent('marker-edit', { 319 | detail: { 320 | name: this.markName, 321 | remark: this.markRemark, 322 | type: this.activeMarker.type, 323 | pos: pixel 324 | } 325 | }) 326 | window.dispatchEvent(evt) 327 | } 328 | } 329 | drop(id) { 330 | const mm = this.manager.get(id); 331 | mm && mm.destroy(); 332 | this.manager.delete(id); 333 | } 334 | rename(id, name) { 335 | const mm = this.manager.get(id); 336 | mm.mname = name; 337 | } 338 | select(type, id, status) { 339 | if (Cesium.defined(id)) { 340 | const manager = this.manager.get(id); 341 | if (Cesium.defined(manager)) { 342 | manager.show = status; 343 | } 344 | } 345 | if (Cesium.defined(type)) { 346 | const values = this.manager.values(); 347 | for (let v of values) { 348 | if (v.mtype === type) { 349 | v.show = status; 350 | } 351 | } 352 | } 353 | } 354 | destroyPopPanle() { 355 | if (this.popDiv) { 356 | $(this.popDiv).remove() 357 | this.popDiv = undefined; 358 | } 359 | } 360 | destroy() { 361 | this.removeAll() 362 | this.destroyPopPanle() 363 | if (!this.pickHandler.isDestroyed()) { 364 | this.pickHandler.destroy() 365 | } 366 | if (!this.handler.isDestroyed()) { 367 | this.handler.destroy() 368 | } 369 | this._viewer = undefined 370 | this.labelOptions = undefined 371 | this.markerOptions = undefined; 372 | this.modelOptions = undefined 373 | } 374 | createPopPanel() { 375 | if (!Cesium.defined(this.selectedMarker)) { 376 | return; 377 | } 378 | if (this.popDiv) { 379 | this.destroyPopPanle(); 380 | } 381 | const popdiv = document.createElement("div"); 382 | popdiv.id = "popContainer"; 383 | popdiv.className = "marker-popWin-class"; 384 | const position = this.selectedMarker.position; 385 | this.popWinPosition = position; 386 | const coord = cvt.cartesian2Degrees(position, this._viewer); 387 | popdiv.style.display = "none"; 388 | // const txtdiv = document.createElement("span"); 389 | // txtdiv.innerText = "名称:" + (this.selectedMarker.name || "未命名"); 390 | let remarkdiv 391 | remarkdiv = document.createElement('span') 392 | remarkdiv.title = this.selectedMarker.description 393 | remarkdiv.innerText = "描述:" + 394 | setString(this.selectedMarker.description || '暂无', 14) 395 | const coordsdiv = document.createElement("span"); 396 | coordsdiv.innerText = 397 | "纬度:" + coord.lon.toFixed(2) + " 纬度:" + coord.lat.toFixed(2); 398 | const arrow = document.createElement("div"); 399 | arrow.className = "arrow"; 400 | const closebtn = document.createElement("span"); 401 | closebtn.className = "iconfont iconclose closebtn"; 402 | const self = this; 403 | closebtn.onclick = function() { 404 | $(self.popDiv).remove() 405 | self.popDiv = undefined; 406 | }; 407 | popdiv.appendChild(closebtn); 408 | // popdiv.appendChild(txtdiv); 409 | if (remarkdiv) { 410 | popdiv.appendChild(remarkdiv) 411 | } 412 | popdiv.appendChild(coordsdiv); 413 | popdiv.appendChild(arrow); 414 | this.popDiv = popdiv; 415 | this._viewer.container.appendChild(this.popDiv); 416 | } 417 | 418 | import(feat) { 419 | if (feat.geometry.type.toUpperCase() !== "POINT") { 420 | throw new Error("无效的数据类型."); 421 | } 422 | const id = this.generateId(); 423 | let marker; 424 | if (feat.properties.mtype === GraphicType.LABEL) { 425 | const lopts = CesiumLabel.defaultStyle; 426 | lopts.position = Cesium.Cartesian3.fromDegrees( 427 | ...feat.geometry.coordinates 428 | ); 429 | lopts.text = feat.properties.name; 430 | marker = new CesiumLabel(this._viewer, lopts); 431 | } else { 432 | const coord = { 433 | lon: feat.geometry.coordinates[0], 434 | lat: feat.geometry.coordinates[1], 435 | height: feat.geometry.coordinates[2] 436 | }; 437 | marker = CesiumBillboard.fromDegrees(this._viewer, coord); 438 | } 439 | marker.mname = feat.properties.name; 440 | marker.mid = id; 441 | this.manager.set(id, marker); 442 | const evt = new CustomEvent('marker-add', { 443 | detail: { 444 | id: marker.mid, 445 | name: marker.mname || '未命名', 446 | type: marker.mtype, 447 | description: marker.description, 448 | properties: feat.properties, 449 | position: { 450 | lon: feat.geometry.coordinates[0], 451 | lat: feat.geometry.coordinates[1], 452 | height: feat.geometry.coordinates[2] 453 | } 454 | } 455 | }) 456 | window.dispatchEvent(evt) 457 | } 458 | addMarker(marker) { 459 | this.manager.set(marker.mid, marker) 460 | } 461 | export (type) { 462 | const managers = this.manager.values(); 463 | const json = { 464 | type: "FeatureCollection", 465 | name: "graphic", 466 | crs: { 467 | type: "name", 468 | properties: { 469 | name: "urn:ogc:def:crs:OGC:1.3:CRS84" 470 | } 471 | }, 472 | features: [] 473 | }; 474 | 475 | for (let m of managers) { 476 | if (m.type === type) { 477 | json.features.push(m.toGeoJson()); 478 | } 479 | } 480 | const blob = new Blob([JSON.stringify(json)], { 481 | type: "" 482 | }); 483 | 484 | window.saveAs(blob, type + parseInt(Cesium.getTimestamp()) + ".geojson"); 485 | } 486 | set font(font) { 487 | this.labelOptions.font = font 488 | if (this.activeMarker) { 489 | this.activeMarker.font = font; 490 | } 491 | } 492 | get font() { 493 | if (this.activeMarker) { 494 | return this.activeMarker.font; 495 | } 496 | return undefined; 497 | } 498 | set color(color) { 499 | this.labelOptions.fillColor = color; 500 | if (this.activeMarker) { 501 | this.activeMarker.color = color; 502 | } 503 | } 504 | set label(option) { 505 | const keys = Object.keys(option); 506 | for (let key of keys) { 507 | this.labelOptions[key] = option[key]; 508 | } 509 | // this.modelAndLabelOptions=[...this.modelAndLabelOptions,...option] 510 | if (this.activeMarker) { 511 | this.activeMarker.setLabel(this.labelOptions); 512 | } 513 | } 514 | set model(options) { 515 | this.modelOptions = { 516 | ...this.modelOptions, 517 | ...options 518 | }; 519 | if (this.activeMarker) { 520 | if (options.uri) { 521 | this.activeMarker.uri = options.uri; 522 | } 523 | if (options.color) { 524 | this.activeMarker.color = options.color; 525 | } 526 | if (options.mode != undefined) { 527 | this.activeMarker.model = options.mode; 528 | } 529 | if (options.mixed != undefined) { 530 | this.activeMarker.mixed = options.mixed; 531 | } 532 | } 533 | } 534 | removeAll() { 535 | const values = this.manager.values(); 536 | for (let v of values) { 537 | v.remove(); 538 | v.destroy(); 539 | } 540 | this.manager.clear(); 541 | } 542 | cancelMark() { 543 | this.activeMarker && this.activeMarker.destroy(); 544 | this.activeMarker = undefined; 545 | this.cursorTip.visible = false; 546 | const evt = new CustomEvent('marker-delete', { 547 | detail: { 548 | id: this.markerid 549 | } 550 | }) 551 | window.dispatchEvent(evt) 552 | this.markName = ""; 553 | this.markRemark = ""; 554 | this.manager.delete(this.markerid); 555 | this.markerid = undefined; 556 | this.removeEventListener(); 557 | } 558 | update(name, remark) { 559 | this.markName = name; 560 | this.markRemark = remark 561 | this.activeMarker.updateText(this.markName, this.markRemark); 562 | this.cursorTip.visible = false; 563 | this.activeMarker.stopEdit() 564 | const evt = new CustomEvent('marker-update', { 565 | detail: { 566 | id: this.activeMarker.mid, 567 | name: this.activeMarker.mname, 568 | description: this.activeMarker.description, 569 | position: cvt.toDegrees(this.activeMarker.position, this._viewer) 570 | } 571 | }) 572 | window.dispatchEvent(evt) 573 | this.activeMarker = undefined; 574 | this.removeEventListener(); 575 | } 576 | generateId() { 577 | return ( 578 | (Math.random() * 10000000).toString(16).substr(0, 4) + 579 | "-" + 580 | new Date().getTime() + 581 | "-" + 582 | Math.random() 583 | .toString() 584 | .substr(2, 5) 585 | ); 586 | } 587 | } 588 | -------------------------------------------------------------------------------- /src/js/utils.js: -------------------------------------------------------------------------------- 1 | import $ from "jquery"; 2 | import * as Cesium from 'cesium' 3 | 4 | /** 5 | * 6 | Cesium坐标转换工具 7 | */ 8 | const CVT = (function() { 9 | function _() {} 10 | _.cartesian2Pixel = function(cartesian, viewer) { 11 | return Cesium.SceneTransforms.wgs84ToWindowCoordinates( 12 | viewer.scene, 13 | cartesian 14 | ); 15 | }; 16 | _.pixel2Cartesian = function(pixel, viewer) { 17 | if (viewer.terrainProvider instanceof Cesium.EllipsoidTerrainProvider) { 18 | return _.pixel2Cartesian1(pixel, viewer); 19 | } else { 20 | return _.pixel2Cartesian2(pixel, viewer); 21 | } 22 | }; 23 | /** 24 | * 二维坐标,没有添加地形数据时调用 25 | */ 26 | _.pixel2Cartesian1 = function(pixel, viewer) { 27 | const cartesian = viewer.camera.pickEllipsoid( 28 | pixel, 29 | viewer.scene.globe.ellipsoid 30 | ); 31 | return cartesian; 32 | }; 33 | /** 34 | * 三维坐标,添加地形数据时调用 35 | */ 36 | _.pixel2Cartesian2 = function(pixel, viewer) { 37 | const ray = viewer.camera.getPickRay(pixel); 38 | const cartesian = viewer.scene.globe.pick(ray, viewer.scene); 39 | return cartesian; 40 | }; 41 | _.cartesian2Radians = function(cartesian, viewer) { 42 | const ellipsoid = viewer.scene.globe.ellipsoid || Cesium.Ellipsoid.WGS84; 43 | const cartographic = Cesium.Cartographic.fromCartesian( 44 | cartesian, 45 | ellipsoid 46 | ); 47 | const lon = cartographic.longitude; 48 | const lat = cartographic.latitude; 49 | const height = cartographic.height; 50 | return { 51 | lon, 52 | lat, 53 | height 54 | }; 55 | }; 56 | _.cartesian2Degrees = function(cartesian, viewer) { 57 | const coords = _.cartesian2Radians(cartesian, viewer); 58 | const lon = Cesium.Math.toDegrees(coords.lon); 59 | const lat = Cesium.Math.toDegrees(coords.lat); 60 | const height = coords.height; 61 | return { 62 | lon, 63 | lat, 64 | height 65 | }; 66 | }; 67 | _.degrees2Cartesian = function(position) { 68 | const cartesian = Cesium.Cartesian3.fromDegrees( 69 | position.lon, 70 | position.lat, 71 | position.height 72 | ); 73 | return cartesian; 74 | }; 75 | _.radians2Cartesian = function(position) { 76 | return Cesium.Cartesian3.fromRadians( 77 | position.lon, 78 | position.lat, 79 | position.height 80 | ); 81 | }; 82 | _.pixel2Degrees = function(pixel, viewer) { 83 | const cartesian = _.pixel2Cartesian(pixel, viewer); 84 | if (Cesium.defined(cartesian)) { 85 | return _.cartesian2Degrees(cartesian, viewer); 86 | } 87 | return undefined; 88 | }; 89 | _.pixel2Radians = function(pixel, viewer) { 90 | const cartesian = _.pixel2Cartesian(pixel, viewer); 91 | if (Cesium.defined(cartesian)) { 92 | return _.cartesian2Radians(cartesian, viewer); 93 | } 94 | return undefined; 95 | }; 96 | _.toDegrees = function(position, viewer) { 97 | if (position instanceof Cesium.Cartesian3) { 98 | return _.cartesian2Degrees(position, viewer); 99 | } else if (position instanceof Cesium.Cartesian2) { 100 | return _.pixel2Degrees(position, viewer); 101 | } 102 | }; 103 | _.toRadians = function(position, viewer) { 104 | if (position instanceof Cesium.Cartesian3) { 105 | return _.cartesian2Radians(position, viewer); 106 | } else if (position instanceof Cesium.Cartesian2) { 107 | return _.pixel2Radians(position, viewer); 108 | } 109 | }; 110 | _.toPixel = function(position, viewer) { 111 | if (position instanceof Cesium.Cartesian3) { 112 | return _.cartesian2Pixel(position, viewer); 113 | } 114 | }; 115 | return _; 116 | })(); 117 | /** 118 | * 119 | * @param {*} container 要移到的元素 120 | * @param {*} target 监听鼠标事件的元素,一般是标题栏 121 | */ 122 | function moveDiv(container, target) { 123 | if (!$("#" + container)) { 124 | throw new Error("请指定要操作的元素"); 125 | } 126 | if (!target) { 127 | target = container; 128 | } 129 | $("#" + target).mousedown(function( 130 | e //e鼠标事件 131 | ) { 132 | $("#" + target).css("cursor", "move"); //改变鼠标指针的形状 133 | // let offset = $("#" + container).offset(); //DIV在页面的位置 134 | let offset = $("#" + container).position(); //DIV在页面的位置 135 | let x = e.pageX - offset.left; //获得鼠标指针离DIV元素左边界的距离 136 | let y = e.pageY - offset.top; //获得鼠标指针离DIV元素上边界的距离 137 | $(document).bind("mousemove", function( 138 | ev //绑定鼠标的移动事件,因为光标在DIV元素外面也要有效果,所以要用doucment的事件,而不用DIV元素的事件 139 | ) { 140 | $("#" + target).css("cursor", "move"); 141 | $("#" + container).stop(); //加上这个之后 142 | let _x = ev.pageX - x; //获得X轴方向移动的值 143 | let _y = ev.pageY - y; //获得Y轴方向移动的值 144 | $("#" + container).animate({ 145 | left: _x + "px", 146 | top: _y + "px" 147 | }, 10); 148 | }); 149 | }); 150 | $(document).mouseup(function() { 151 | $("#" + target).css("cursor", "default"); 152 | $(this).unbind("mousemove"); 153 | }); 154 | } 155 | /** 156 | * 获得当前视野范围 157 | * @param {Viewer} viewer 158 | */ 159 | function currentExtent(viewer) { 160 | // 范围对象 161 | const extent = {}; 162 | 163 | // 得到当前三维场景 164 | const scene = viewer.scene; 165 | 166 | // 得到当前三维场景的椭球体 167 | const ellipsoid = scene.globe.ellipsoid; 168 | const canvas = scene.canvas; 169 | 170 | // canvas左上角 171 | const car3_lt = viewer.camera.pickEllipsoid( 172 | new Cesium.Cartesian2(0, 0), 173 | ellipsoid 174 | ); 175 | 176 | // canvas右下角 177 | const car3_rb = viewer.camera.pickEllipsoid( 178 | new Cesium.Cartesian2(canvas.width, canvas.height), 179 | ellipsoid 180 | ); 181 | 182 | // 当canvas左上角和右下角全部在椭球体上 183 | if (car3_lt && car3_rb) { 184 | const carto_lt = ellipsoid.cartesianToCartographic(car3_lt); 185 | const carto_rb = ellipsoid.cartesianToCartographic(car3_rb); 186 | extent.xmin = Cesium.Math.toDegrees(carto_lt.longitude); 187 | extent.ymax = Cesium.Math.toDegrees(carto_lt.latitude); 188 | extent.xmax = Cesium.Math.toDegrees(carto_rb.longitude); 189 | extent.ymin = Cesium.Math.toDegrees(carto_rb.latitude); 190 | } 191 | 192 | // 当canvas左上角不在但右下角在椭球体上 193 | else if (!car3_lt && car3_rb) { 194 | let car3_lt2 = null; 195 | let yIndex = 0; 196 | do { 197 | // 这里每次10像素递加,一是10像素相差不大,二是为了提高程序运行效率 198 | yIndex <= canvas.height ? (yIndex += 10) : canvas.height; 199 | car3_lt2 = viewer.camera.pickEllipsoid( 200 | new Cesium.Cartesian2(0, yIndex), 201 | ellipsoid 202 | ); 203 | } while (!car3_lt2); 204 | const carto_lt2 = ellipsoid.cartesianToCartographic(car3_lt2); 205 | const carto_rb2 = ellipsoid.cartesianToCartographic(car3_rb); 206 | extent.xmin = Cesium.Math.toDegrees(carto_lt2.longitude); 207 | extent.ymax = Cesium.Math.toDegrees(carto_lt2.latitude); 208 | extent.xmax = Cesium.Math.toDegrees(carto_rb2.longitude); 209 | extent.ymin = Cesium.Math.toDegrees(carto_rb2.latitude); 210 | } 211 | 212 | // 获取高度 213 | extent.height = Math.ceil(viewer.camera.positionCartographic.height); 214 | return extent; 215 | } 216 | /** 217 | * 获得当前视野中心 218 | * @param {*} viewer 219 | */ 220 | const viewCenter = ( 221 | viewer, 222 | inWorldCoordinates = true, 223 | result = new Cesium.Cartesian3() 224 | ) => { 225 | const scene = viewer.scene; 226 | const camera = viewer.camera; 227 | const unprojectedScratch = new Cesium.Cartographic(); 228 | const rayScratch = new Cesium.Ray(); 229 | 230 | if (scene.mode === Cesium.SceneMode.MORPHING) { 231 | return undefined; 232 | } 233 | 234 | // TODO bug when tracking: if entity moves the current position should be used and not only the one when starting orbiting/rotating 235 | // TODO bug when tracking: reset should reset to default view of tracked entity 236 | 237 | if (Cesium.defined(viewer.trackedEntity)) { 238 | result = viewer.trackedEntity.position.getValue( 239 | viewer.clock.currentTime, 240 | result 241 | ); 242 | } else { 243 | rayScratch.origin = camera.positionWC; 244 | rayScratch.direction = camera.directionWC; 245 | result = scene.globe.pick(rayScratch, scene, result); 246 | } 247 | 248 | if (!Cesium.defined(result)) { 249 | return undefined; 250 | } 251 | 252 | if ( 253 | scene.mode === Cesium.SceneMode.SCENE2D || 254 | scene.mode === Cesium.SceneMode.COLUMBUS_VIEW 255 | ) { 256 | result = camera.worldToCameraCoordinatesPoint(result, result); 257 | 258 | if (inWorldCoordinates) { 259 | result = scene.globe.ellipsoid.cartographicToCartesian( 260 | scene.mapProjection.unproject(result, unprojectedScratch), 261 | result 262 | ); 263 | } 264 | } else { 265 | if (!inWorldCoordinates) { 266 | result = camera.worldToCameraCoordinatesPoint(result, result); 267 | } 268 | } 269 | 270 | return result; 271 | }; 272 | const saveCurViewerImage = (viewer, filename) => { 273 | viewer.render(); 274 | if (!filename || filename == "") { 275 | filename = new Date().toLocaleString() + ".png"; 276 | } 277 | const ext = filename.split(".")[1]; 278 | downloadFile(filename, viewer.scene.canvas.toDataURL("image/%s" % ext)); 279 | }; 280 | const downloadFile = (fileName, content) => { 281 | //下载文件 282 | let aLink = document.createElement("a"); 283 | let blob = base64ToBlob(content); //new Blob([content]); 284 | let evt = document.createEvent("HTMLEvents"); 285 | evt.initEvent("click", true, true); //initEvent 不加后两个参数在FF下会报错 事件类型,是否冒泡,是否阻止浏览器的默认行为 286 | aLink.download = fileName; 287 | aLink.href = URL.createObjectURL(blob); 288 | aLink.click(); 289 | 290 | function base64ToBlob(code) { 291 | //base64转blob 292 | let parts = code.split(";base64,"); 293 | let contentType = parts[0].split(":")[1]; 294 | let raw = window.atob(parts[1]); 295 | let rawLength = raw.length; 296 | 297 | let uInt8Array = new Uint8Array(rawLength); 298 | 299 | for (let i = 0; i < rawLength; ++i) { 300 | uInt8Array[i] = raw.charCodeAt(i); 301 | } 302 | return new Blob([uInt8Array], { 303 | type: contentType 304 | }); 305 | } 306 | }; 307 | const errroCatch = function(e, callback) { 308 | if (e.response) { 309 | callback(e.response.data); 310 | } else if (e.request) { 311 | callback(e.request); 312 | } else { 313 | callback(e.message); 314 | } 315 | }; 316 | class CursorTip { 317 | constructor(text, id, viewer) { 318 | const tooltip = document.createElement("div"); 319 | tooltip.id = id || "cursor-tip"; 320 | tooltip.className = "cursor-tip-class"; 321 | tooltip.innerHTML = text; 322 | document.body.appendChild(tooltip); 323 | this.ele = tooltip; 324 | this._visible = true; 325 | const self = this; 326 | if (viewer instanceof Cesium.Viewer) { 327 | viewer.screenSpaceEventHandler.setInputAction(e => { 328 | self.updatePosition(e.endPosition); 329 | }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); 330 | } 331 | } 332 | updatePosition(pixel) { 333 | this.ele.style.left = pixel.x + 10 + "px"; 334 | this.ele.style.top = pixel.y + 10 + "px"; 335 | } 336 | updateText(text) { 337 | this.ele.innerHTML = text; 338 | } 339 | get visible() { 340 | return this._visible; 341 | } 342 | set visible(v) { 343 | this._visible = v; 344 | if (v) { 345 | this.ele.style.display = "block"; 346 | } else { 347 | this.ele.style.display = "none"; 348 | } 349 | } 350 | } 351 | 352 | function checkViewer(viewer) { 353 | if (viewer instanceof Cesium.Viewer === false) { 354 | throw new CesiumDrawError(viewer + "不是一个有效的Cesium Viewer对象") 355 | } 356 | } 357 | 358 | function checkComponent(component, object) { 359 | if (component && component._viewer instanceof Cesium.Viewer === false) { 360 | throw new CesiumDrawError('组件尚未初始化' + component._uid) 361 | } else if (!component && !Cesium.defined(object)) { 362 | throw new CesiumDrawError('组件尚未初始化') 363 | } 364 | } 365 | class CesiumDrawError extends Error { 366 | constructor(message) { 367 | super(message); 368 | this.name = 'CesiumDrawError' 369 | } 370 | } 371 | 372 | function pointVisibilityOnEarth(point, viewer) { 373 | return new Cesium.EllipsoidalOccluder(Cesium.Ellipsoid.WGS84, viewer.camera.position) 374 | .isPointVisible(point); 375 | } 376 | export { 377 | moveDiv, 378 | errroCatch, 379 | currentExtent, 380 | viewCenter, 381 | saveCurViewerImage, 382 | downloadFile, 383 | pointVisibilityOnEarth, 384 | CVT, 385 | CursorTip, 386 | checkComponent, 387 | checkViewer 388 | }; 389 | export default { 390 | moveDiv, 391 | errroCatch, 392 | currentExtent, 393 | viewCenter, 394 | saveCurViewerImage, 395 | downloadFile, 396 | CVT, 397 | CursorTip, 398 | pointVisibilityOnEarth 399 | }; 400 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | // import 'default-passive-events' 4 | import "@/assets/css/iconfont.css"; 5 | import ElementPlus from 'element-plus' 6 | import 'element-plus/dist/index.css' 7 | 8 | const app = createApp(App); 9 | app.use(ElementPlus); 10 | app.mount('#app') 11 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | const { defineConfig } = require("@vue/cli-service"); 2 | const { DefinePlugin } = require('webpack'); 3 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 4 | const CopyWebpackPlugin = require('copy-webpack-plugin'); 5 | const theme = "dark"; 6 | const plugins = []; 7 | const externals = { 8 | } 9 | if (process.env.NODE_ENV === 'production') { 10 | plugins.push(new MiniCssExtractPlugin({ 11 | filename: `theme/${theme}.css`, 12 | // chunkFilename: `css/[name].${conf.version}.css` 13 | })) 14 | externals.cesium = 'Cesium'; 15 | } else { 16 | new CopyWebpackPlugin({ 17 | patterns: [ 18 | { from: 'node_modules/cesium/Build/Cesium/Workers', to: 'Workers' }, 19 | { from: 'node_modules/cesium/Build/Cesium/ThirdParty', to: 'ThirdParty' }, 20 | { from: 'node_modules/cesium/Build/Cesium/Assets', to: 'Assets' }, 21 | { from: 'node_modules/cesium/Build/Cesium/Widgets', to: 'Widgets' }, 22 | ] 23 | }), 24 | new DefinePlugin({ 25 | CESIUM_BASE_URL: JSON.stringify('') 26 | }) 27 | } 28 | 29 | module.exports = defineConfig({ 30 | outputDir: "dist", 31 | transpileDependencies: true, 32 | lintOnSave: true, 33 | assetsDir: 'theme', 34 | configureWebpack: { 35 | plugins, 36 | externals 37 | 38 | }, 39 | css: { 40 | loaderOptions: { 41 | sass: { 42 | additionalData: ` 43 | @import "@/assets/css/theme/${theme}.scss"; 44 | `, 45 | }, 46 | }, 47 | // 启用 CSS modules 48 | // modules: false, 49 | // 是否使用css分离插件 50 | extract: true, 51 | // 开启 CSS source maps,一般不建议开启 52 | sourceMap: false 53 | // css预设器配置项 54 | // 是否使用css分离插件 ExtractTextPlugin 55 | // extract: { 56 | // //一种方式,打包后的css 会带版本号,不改变文件名的。 57 | // filename: 'theme/[name].css' 58 | // } 59 | } 60 | }); --------------------------------------------------------------------------------