├── env.d.ts
├── public
└── favicon.ico
├── src
├── assets
│ └── start.png
├── main.ts
├── js
│ ├── prompt
│ │ ├── prompt.css
│ │ └── prompt.js
│ ├── util.js
│ └── plot
│ │ ├── createPoint.js
│ │ ├── createBillboard.js
│ │ ├── createGltfModel.js
│ │ ├── createRectangle.js
│ │ ├── createLabel.js
│ │ ├── createPolyline.js
│ │ ├── createPolygon.js
│ │ ├── createRectgon.js
│ │ ├── basePlot.js
│ │ ├── createCircle.js
│ │ └── drawTool.js
└── Index.vue
├── tsconfig.json
├── tsconfig.node.json
├── tsconfig.app.json
├── vite.config.ts
├── .gitignore
├── index.html
├── package.json
└── README.md
/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gitgitczl/cesiumExp-plot/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/src/assets/start.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gitgitczl/cesiumExp-plot/HEAD/src/assets/start.png
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./tsconfig.node.json"
6 | },
7 | {
8 | "path": "./tsconfig.app.json"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import App from './Index.vue'
3 | import Antd from "ant-design-vue";
4 | import "ant-design-vue/dist/antd.css";
5 | const app = createApp(App)
6 | app.use(Antd);
7 | app.mount('#app')
8 |
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@tsconfig/node18/tsconfig.json",
3 | "include": ["vite.config.*", "vitest.config.*", "cypress.config.*", "playwright.config.*"],
4 | "compilerOptions": {
5 | "composite": true,
6 | "module": "ESNext",
7 | "types": ["node"]
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@vue/tsconfig/tsconfig.dom.json",
3 | "include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
4 | "exclude": ["src/**/__tests__/*"],
5 | "compilerOptions": {
6 | "composite": true,
7 | "baseUrl": ".",
8 | "paths": {
9 | "@/*": ["./src/*"]
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { fileURLToPath, URL } from 'node:url'
2 |
3 | import { defineConfig } from 'vite'
4 | import vue from '@vitejs/plugin-vue'
5 |
6 | // https://vitejs.dev/config/
7 | export default defineConfig({
8 | plugins: [vue()],
9 | resolve: {
10 | alias: {
11 | '@': fileURLToPath(new URL('./src', import.meta.url))
12 | }
13 | }
14 | })
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | .DS_Store
12 | dist
13 | dist-ssr
14 | coverage
15 | *.local
16 |
17 | /cypress/videos/
18 | /cypress/screenshots/
19 |
20 | # Editor directories and files
21 | .vscode/*
22 | !.vscode/extensions.json
23 | .idea
24 | *.suo
25 | *.ntvs*
26 | *.njsproj
27 | *.sln
28 | *.sw?
29 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
85 |
86 | ${this.opt.content}
87 |
88 |
89 |
90 | ${anchorHtml}
91 |
92 | ${closeHtml}
93 | `;
94 | // 构建弹窗元素
95 | this.promptDiv = window.document.createElement("div");
96 | this.promptDiv.className = `easy3d-prompt ${this.opt.className}`;
97 | this.promptDiv.id = promptId;
98 | this.promptDiv.innerHTML = promptConenet;
99 | let mapDom = window.document.getElementById(mapid);
100 | mapDom.appendChild(this.promptDiv);
101 | const clsBtn = window.document.getElementById(`prompt-close-${this.opt.id}`);
102 | let that = this;
103 | if (clsBtn) {
104 | clsBtn.addEventListener("click", (e) => {
105 | that.hide();
106 | if (that.opt.close) that.opt.close();
107 | })
108 | }
109 |
110 | /**
111 | * @property {Object} promptDom 弹窗div
112 | */
113 | this.promptDom = window.document.getElementById(promptId);
114 |
115 | this.position = this.transPosition(this.opt.position);
116 | // ====================== 创建弹窗内容 end ======================
117 |
118 | if (promptType == 2) this.bindRender(); // 固定位置弹窗 绑定实时渲染 当到地球背面时 隐藏
119 | if (this.opt.show == false) this.hide();
120 | this.containerW = this.viewer.container.offsetWidth;
121 | this.containerH = this.viewer.container.offsetHeight;
122 | this.containerLeft = this.viewer.container.offsetLeft;
123 | this.containerTop = this.viewer.container.offsetTop;
124 |
125 | /**
126 | * @property {Number} contentW 弹窗宽度
127 | */
128 | this.contentW = Math.ceil(Number(this.promptDom.offsetWidth)); // 宽度
129 |
130 | /**
131 | * @property {Number} contentH 弹窗高度
132 | */
133 | this.contentH = this.promptDom.offsetHeight; // 高度
134 |
135 | if (this.opt.success) this.opt.success();
136 | }
137 |
138 | /**
139 | * 销毁
140 | */
141 | destroy() {
142 | if (this.promptDiv) {
143 | window.document.getElementById(this.viewer.container.id).removeChild(this.promptDiv);
144 | this.promptDiv = null;
145 | }
146 | if (this.rendHandler) {
147 | this.rendHandler();
148 | this.rendHandler = null;
149 | }
150 | }
151 | // 实时监听
152 | bindRender() {
153 | let that = this;
154 | this.rendHandler = this.viewer.scene.postRender.addEventListener(function () {
155 | if (!that.isShow && that.promptDom) {
156 | that.promptDom.style.display = "none";
157 | return;
158 | }
159 | if (!that.position) return;
160 | if (that.position instanceof Cesium.Cartesian3) {
161 | let px = Cesium.SceneTransforms.wgs84ToWindowCoordinates(that.viewer.scene, that.position);
162 | if (!px) return;
163 | const occluder = new Cesium.EllipsoidalOccluder(that.viewer.scene.globe.ellipsoid, that.viewer.scene.camera.position);
164 | // 当前点位是否可见 是否在地球背面
165 | const res = occluder.isPointVisible(that.position);
166 | if (res) {
167 | if (that.promptDom) that.promptDom.style.display = "block";
168 | } else {
169 | if (that.promptDom) that.promptDom.style.display = "none";
170 | }
171 | that.setByPX({
172 | x: px.x,
173 | y: px.y
174 | });
175 | } else {
176 | that.setByPX({
177 | x: that.position.x,
178 | y: that.position.y
179 | });
180 | }
181 |
182 | }, this);
183 | }
184 |
185 | /**
186 | *
187 | * @param {Cesium.Cartesian3 | Object} px 弹窗坐标
188 | * @param {String} html 弹窗内容
189 | */
190 | update(px, html) {
191 | if (px instanceof Cesium.Cartesian3) {
192 | this.position = px.clone();
193 | px = Cesium.SceneTransforms.wgs84ToWindowCoordinates(this.viewer.scene, px);
194 | }
195 | this.contentW = Math.ceil(Number(this.promptDom.offsetWidth)); // 宽度
196 | this.contentH = this.promptDom.offsetHeight; // 高度
197 | if (px) this.setByPX(px);
198 | if (html) this.setContent(html);
199 | }
200 |
201 | // 判断是否在当前视野内
202 | isInView() {
203 | if (!this.position) return false;
204 | let px = null;
205 | if (this.position instanceof Cesium.Cartesian2) {
206 | px = this.position;
207 | } else {
208 | px = Cesium.SceneTransforms.wgs84ToWindowCoordinates(this.viewer.scene, this.position);
209 | }
210 | const occluder = new Cesium.EllipsoidalOccluder(this.viewer.scene.globe.ellipsoid, this.viewer.scene.camera.position);
211 | // 是否在地球背面
212 | const res = occluder.isPointVisible(this.position);
213 | let isin = false;
214 | if (!px) return isin;
215 | if (
216 | px.x > this.containerLeft &&
217 | px.x < (this.containerLeft + this.containerW) &&
218 | px.y > this.containerTop &&
219 | px.y < (this.containerTop + this.containerH)
220 | ) {
221 | isin = true;
222 | }
223 | return res && isin;
224 | }
225 |
226 | /**
227 | * 是否可见
228 | * @param {Boolean} isShow true可见,false不可见
229 | */
230 | setVisible(isShow) {
231 | let isin = this.isInView(this.position);
232 | if (isin && isShow) {
233 | this.isShow = true;
234 | if (this.promptDom) this.promptDom.style.display = "block";
235 | } else {
236 | this.isShow = false;
237 | if (this.promptDom) this.promptDom.style.display = "none";
238 | }
239 | }
240 |
241 | /**
242 | * 显示
243 | */
244 | show() {
245 | this.setVisible(true);
246 | }
247 |
248 | /**
249 | * 隐藏
250 | */
251 | hide() {
252 | this.setVisible(false);
253 | }
254 |
255 | /**
256 | * 设置弹窗内容
257 | * @param {String} content 内容
258 | */
259 | setContent(content) {
260 | let pc = window.document.getElementById(`prompt-content-${this.opt.id}`);
261 | pc.innerHTML = content;
262 | }
263 |
264 | /**
265 | * 设置弹窗坐标
266 | * @param {Object} opt 屏幕坐标
267 | */
268 | setByPX(opt) {
269 | if (!opt) return;
270 | if (this.promptDom) {
271 | const contentW = this.promptDom.offsetWidth; // 宽度
272 | const contentH = this.promptDom.offsetHeight; // 高度
273 | if (this.opt.type == 1) {
274 | this.promptDom.style.left = ((Number(opt.x) + Number(this.opt.offset.x || 0))) + "px";
275 | this.promptDom.style.top = ((Number(opt.y) + Number(this.opt.offset.y || 0))) + "px";
276 | } else {
277 | this.promptDom.style.left = ((Number(opt.x) + Number(this.opt.offset.x || 0)) - Number(this.contentW) / 2) + "px";
278 | this.promptDom.style.top = ((Number(opt.y) + Number(this.opt.offset.y || 0)) - Number(this.contentH)) + "px";
279 | }
280 | }
281 | }
282 |
283 | // 坐标转换
284 | transPosition(p) {
285 | let position;
286 | if (Array.isArray(p)) {
287 | const posi = Cesium.Cartesian3.fromDegrees(p[0], p[1], p[2] || 0);
288 | position = posi.clone();
289 | } else if (p instanceof Cesium.Cartesian3) {
290 | position = p.clone();
291 | } else { // 像素类型
292 | position = p;
293 | }
294 | return position;
295 | }
296 | }
297 |
298 | export default Prompt;
--------------------------------------------------------------------------------
/src/js/plot/basePlot.js:
--------------------------------------------------------------------------------
1 | // 所有标绘类的父类
2 | import util from "../util";
3 | /**
4 | * 标绘基类
5 | * @description 标绘基类,一般不直接实例化,而实例化其子类(见下方Classes)
6 | * @class
7 | * @alias BasePlot
8 | */
9 | class BasePlot {
10 | /**
11 | * @param {Cesium.Viewer} viewer 地图viewer对象
12 | * @param {Object} style 样式属性
13 | */
14 | constructor(viewer, style) {
15 | this.viewer = viewer;
16 |
17 | /**
18 | * @property {Object} style 样式
19 | */
20 | this.style = style || {};
21 |
22 | /**
23 | * @property {String | Number} objId 唯一id
24 | */
25 | this.objId = Number((new Date()).getTime() + "" + Number(Math.random() * 1000).toFixed(0));
26 | this.handler = undefined;
27 | this.modifyHandler = undefined;
28 |
29 | /**
30 | * @property {String} type 类型
31 | */
32 | this.type = '';
33 | /**
34 | *@property {Cesium.Cartesian3[]} positions 坐标数组
35 | */
36 | this.positions = [];
37 |
38 | /**
39 | *@property {String} state 标识当前状态 no startCreate creating endCreate startEdit endEdit editing
40 | */
41 | this.state = null; //
42 |
43 | /**
44 | * @property {Object} prompt 鼠标提示框
45 | */
46 | this.prompt = null; // 初始化鼠标提示框
47 | this.controlPoints = []; // 控制点
48 | this.modifyPoint = null;
49 |
50 | /**
51 | * 图标entity对象
52 | * @property {Cesium.Entity} entity entity对象
53 | */
54 |
55 | this.entity = null;
56 | this.pointStyle = {};
57 |
58 | /**
59 | * @property {Object} promptStyle 鼠标提示框样式
60 | */
61 | this.promptStyle = this.style.prompt || {
62 | show: true
63 | }
64 | this.properties = {};
65 |
66 | // 缩放分辨率比例
67 | this.clientScale = 1;
68 | }
69 |
70 | /**
71 | *
72 | * @param {Object} px 像素坐标
73 | * @returns {Cesium.Cartesian3} 世界坐标
74 | */
75 | getCatesian3FromPX(px) {
76 | px = this.transpx(px); // 此处单独解决了地图采点的偏移 prompt的偏移暂未处理
77 | let picks = this.viewer.scene.drillPick(px);
78 | this.viewer.scene.render();
79 | let cartesian;
80 | let isOn3dtiles = false;
81 | for (let i = 0; i < picks.length; i++) {
82 | if ((picks[i] && picks[i].primitive) && picks[i].primitive instanceof Cesium.Cesium3DTileset) { //模型上拾取
83 | isOn3dtiles = true;
84 | break;
85 | }
86 | }
87 | if (isOn3dtiles) {
88 | cartesian = this.viewer.scene.pickPosition(px);
89 | } else {
90 | let ray = this.viewer.camera.getPickRay(px);
91 | if (!ray) return null;
92 | cartesian = this.viewer.scene.globe.pick(ray, this.viewer.scene);
93 | }
94 | return cartesian;
95 | }
96 |
97 | /**
98 | * 此方法用于 地图界面缩放问题(transform:translate(2))
99 | * @param {Number} scale 缩放比例
100 | */
101 | setClientScale(scale) {
102 | scale = scale || 1;
103 | this.clientScale = scale;
104 | }
105 |
106 | transpx(px) {
107 | return {
108 | x: px.x / (this.clientScale || 1),
109 | y: px.y / (this.clientScale || 1)
110 | }
111 | }
112 |
113 | /**
114 | *
115 | * @returns {Cesium.Entity} 实体对象
116 | */
117 | getEntity() {
118 | return this.entity;
119 | }
120 |
121 | /**
122 | *
123 | * @param {Boolean} isWgs84 是否转化为经纬度
124 | * @returns {Array} 坐标数组
125 | */
126 | getPositions(isWgs84) {
127 | return isWgs84 ? util.cartesiansToLnglats(this.positions, this.viewer) : this.positions;
128 | }
129 |
130 | /**
131 | * 获取经纬度坐标
132 | * @returns {Array} 经纬度坐标数组
133 | */
134 | getLnglats() {
135 | return util.cartesiansToLnglats(this.positions, this.viewer);
136 | }
137 |
138 | /**
139 | * 设置自定义属性
140 | * @param {Object} prop 属性
141 | */
142 | setOwnProp(prop) {
143 | if (this.entity) this.entity.ownProp = prop;
144 | }
145 |
146 | /**
147 | * 移除当前entity对象
148 | */
149 | remove() {
150 | if (this.entity) {
151 | this.state = "no";
152 | this.viewer.entities.remove(this.entity);
153 | this.entity = null;
154 | }
155 | }
156 |
157 | /**
158 | * 设置entity对象的显示隐藏
159 | * @param {Boolean} visible
160 | */
161 | setVisible(visible) {
162 | if (this.entity) this.entity.show = visible;
163 | }
164 |
165 | // 操作控制
166 | forbidDrawWorld(isForbid) {
167 | this.viewer.scene.screenSpaceCameraController.enableRotate = !isForbid;
168 | this.viewer.scene.screenSpaceCameraController.enableTilt = !isForbid;
169 | this.viewer.scene.screenSpaceCameraController.enableTranslate = !isForbid;
170 | this.viewer.scene.screenSpaceCameraController.enableInputs = !isForbid;
171 | }
172 |
173 | /**
174 | * 销毁
175 | */
176 | destroy() {
177 | if (this.handler) {
178 | this.handler.destroy();
179 | this.handler = null;
180 | }
181 | if (this.modifyHandler) {
182 | this.modifyHandler.destroy();
183 | this.modifyHandler = null;
184 | }
185 | if (this.entity) {
186 | this.viewer.entities.remove(this.entity);
187 | this.entity = null;
188 | }
189 |
190 | this.positions = [];
191 | this.style = null;
192 | for (var i = 0; i < this.controlPoints.length; i++) {
193 | var point = this.controlPoints[i];
194 | this.viewer.entities.remove(point);
195 | }
196 | this.controlPoints = [];
197 | this.modifyPoint = null;
198 | if (this.prompt) {
199 | this.prompt.destroy();
200 | this.prompt = null;
201 | }
202 | this.state = "no";
203 | this.forbidDrawWorld(false);
204 | }
205 |
206 | /**
207 | *
208 | * 开始编辑
209 | */
210 | startEdit(callback) {
211 | if (this.state == "startEdit" || this.state == "editing" || !this.entity) return;
212 | this.state = "startEdit";;
213 | if (!this.modifyHandler) this.modifyHandler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
214 | let that = this;
215 | for (let i = 0; i < that.controlPoints.length; i++) {
216 | let point = that.controlPoints[i];
217 | if (point) point.show = true;
218 | }
219 | this.entity.show = true;
220 |
221 | this.modifyHandler.setInputAction(function (evt) {
222 | if (!that.entity) return;
223 | let pick = that.viewer.scene.pick(evt.position);
224 | if (Cesium.defined(pick) && pick.id) {
225 | if (!pick.id.objId)
226 | that.modifyPoint = pick.id;
227 | that.forbidDrawWorld(true);
228 | }
229 | }, Cesium.ScreenSpaceEventType.LEFT_DOWN);
230 | this.modifyHandler.setInputAction(function (evt) {
231 | if (that.positions.length < 1 || !that.modifyPoint) return;
232 | let cartesian = that.getCatesian3FromPX(evt.endPosition, that.viewer, [that.entity, that.modifyPoint]);
233 | if (cartesian) {
234 | that.modifyPoint.position.setValue(cartesian);
235 | that.positions[that.modifyPoint.wz] = cartesian;
236 | that.state = "editing";
237 | if (callback) callback();
238 | }
239 | }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
240 |
241 | this.modifyHandler.setInputAction(function (evt) {
242 | if (!that.modifyPoint) return;
243 | that.modifyPoint = null;
244 | that.forbidDrawWorld(false);
245 | that.state = "editing";
246 | }, Cesium.ScreenSpaceEventType.LEFT_UP);
247 | }
248 |
249 | /**
250 | * 结束编辑
251 | * @param {Function} callback 回调函数
252 | * @example
253 | * plotObj.endEdit(function(entity){})
254 | */
255 | endEdit(callback) {
256 | for (let i = 0; i < this.controlPoints.length; i++) {
257 | let point = this.controlPoints[i];
258 | if (point) point.show = false;
259 | }
260 | if (this.modifyHandler) {
261 | this.modifyHandler.destroy();
262 | this.modifyHandler = null;
263 | if (callback) callback(this.entity);
264 | }
265 | this.forbidDrawWorld(false);
266 | this.state = "endEdit";
267 | }
268 |
269 | /**
270 | * 结束创建
271 | */
272 | endCreate() {
273 |
274 | }
275 |
276 | /**
277 | * 在当前步骤结束
278 | */
279 | done() {
280 |
281 | }
282 |
283 |
284 | // 构建控制点
285 | createPoint(position) {
286 | if (!position) return;
287 | this.pointStyle.color = this.pointStyle.color || Cesium.Color.CORNFLOWERBLUE;
288 | this.pointStyle.outlineColor = this.pointStyle.color || Cesium.Color.CORNFLOWERBLUE;
289 |
290 | let color = this.pointStyle.color instanceof Cesium.Color ? this.pointStyle.color : Cesium.Color.fromCssColorString(this.pointStyle.color);
291 | color = color.withAlpha(this.pointStyle.colorAlpha || 1);
292 |
293 | let outlineColor = this.pointStyle.outlineColor instanceof Cesium.Color ? this.pointStyle.outlineColor : Cesium.Color.fromCssColorString(this.pointStyle.outlineColor);
294 | outlineColor = outlineColor.withAlpha(this.pointStyle.outlineColorAlpha || 1);
295 |
296 | return this.viewer.entities.add({
297 | position: position,
298 | point: {
299 | pixelSize: this.pointStyle.property || 10,
300 | color: color,
301 | outlineWidth: this.pointStyle.outlineWidth || 0,
302 | outlineColor: outlineColor,
303 | disableDepthTestDistance: Number.POSITIVE_INFINITY
304 | },
305 | show: false
306 | });
307 | }
308 |
309 | // 获取当前标绘的样式
310 | /* getStyle() {
311 | if (!this.entity) return;
312 | let graphic = this.entity[this.plotType];
313 | if (!graphic) return;
314 | let style = {};
315 | switch (this.plotType) {
316 | case 'polyline':
317 | style.clampToGround = graphic.clampToGround._value; // 是否贴地
318 | style.distanceDisplayCondition = graphic.distanceDisplayCondition._value; // 显示控制
319 | style.width = graphic.width._value; // 线宽
320 | let colorObj = this.transfromLineMaterial(graphic.material);
321 | style = Object.assign(style, colorObj);
322 | break;
323 | case "polygon":
324 | style.heightReference = graphic.heightReference.getValue();
325 | style.fill = graphic.fill._value;
326 | style.extrudedHeight = graphic.extrudedHeight._value;
327 | let gonColorObj = this.transfromGonMaterial(graphic.material);
328 | style = Object.assign(style, gonColorObj);
329 |
330 | style.outline = graphic.outline._value;
331 | let ocv = graphic.outlineColor.getValue();
332 | style.outlineColorAlpha = ocv.alpha;
333 | style.outlineColor = new Cesium.Color(ocv.red, ocv.green, ocv.blue, 1).toCssHexString();
334 |
335 | break;
336 | default:
337 | break;
338 | }
339 | return style;
340 | } */
341 |
342 | // 获取线的材质
343 | transfromLineMaterial(material) {
344 | if (!material) return;
345 | let colorObj = {};
346 | if (material instanceof Cesium.Color) {
347 | let colorVal = material.color.getValue();
348 | colorObj.colorAlpha = colorVal.alpha;
349 | // 转为hex
350 | colorObj.colorHex = new Cesium.Color(colorVal.red, colorVal.green, colorVal.blue, 1).toCssHexString();
351 | }
352 | return colorObj;
353 | }
354 |
355 | // 获取面材质
356 | transfromGonMaterial(material) {
357 | if (!material) return;
358 | let colorObj = {};
359 | if (material instanceof Cesium.Color) {
360 | let colorVal = material.color.getValue();
361 | colorObj.colorAlpha = colorVal.alpha;
362 | // 转为hex
363 | colorObj.colorHex = new Cesium.Color(colorVal.red, colorVal.green, colorVal.blue, 1).toCssHexString();
364 | }
365 | return colorObj;
366 | }
367 |
368 | // 设置实体的属性
369 | setAttr(attr) {
370 | this.properties.attr = attr || {};
371 | }
372 |
373 | getAttr(){
374 | return this.properties.attr;
375 | }
376 |
377 | /**
378 | * 缩放至当前绘制的对象
379 | */
380 | zoomTo() {
381 | if (this.entity) {
382 | this.viewer.zoomTo(this.entity);
383 | }
384 | }
385 |
386 |
387 | }
388 |
389 | export default BasePlot;
--------------------------------------------------------------------------------
/src/js/plot/createCircle.js:
--------------------------------------------------------------------------------
1 | import '../prompt/prompt.css'
2 | import Prompt from '../prompt/prompt.js'
3 | import util from '../util.js'
4 | import BasePlot from './basePlot';
5 | /**
6 | * 圆标绘类
7 | * @class
8 | * @augments BasePlot
9 | * @alias BasePlot.CreateCircle
10 | */
11 | class CreateCircle extends BasePlot {
12 | constructor(viewer, style) {
13 | super(viewer, style);
14 | this.type = "circle";
15 | this.objId = Number(
16 | new Date().getTime() + "" + Number(Math.random() * 1000).toFixed(0)
17 | );
18 | this.viewer = viewer;
19 | this.style = style;
20 | this.floatPoint = null;
21 |
22 | /**
23 | * @property {Cesium.Entity} centerPoint 圆中心点
24 | */
25 | this.centerPoint = null;
26 |
27 | /**
28 | * @property {Cesium.Cartesian3} position 圆中心点坐标
29 | */
30 | this.position = null;
31 | this.floatPosition = null;
32 |
33 | /**
34 | * @property {Number} 圆半径
35 | */
36 | this.radius = 0.001;
37 | this.modifyPoint = null;
38 | this.pointArr = [];
39 | }
40 |
41 | /**
42 | * 开始绘制
43 | * @param {Function} callback 绘制成功后回调函数
44 | */
45 | start(callback) {
46 | if (!this.prompt && this.promptStyle.show)
47 | this.prompt = new Prompt(this.viewer, this.promptStyle);
48 | this.state = "startCreate";
49 | let that = this;
50 | if (!this.handler) this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
51 | this.handler.setInputAction(function (evt) {
52 | //单击开始绘制
53 | let cartesian = that.getCatesian3FromPX(evt.position, that.viewer);
54 | if (!cartesian) return;
55 | if (!that.centerPoint) {
56 | that.position = cartesian;
57 | that.centerPoint = that.createPoint(cartesian);
58 | that.centerPoint.typeAttr = "center";
59 |
60 | that.floatPoint = that.createPoint(cartesian.clone());
61 | that.floatPosition = cartesian.clone();
62 | that.floatPoint.typeAttr = "float";
63 | that.entity = that.createCircle(that.position, that.radius);
64 | } else {
65 | if (that.entity) {
66 | that.endCreate();
67 | if (callback) callback(that.entity);
68 | }
69 | }
70 | }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
71 | this.handler.setInputAction(function (evt) {
72 | // 移动时绘制线
73 | if (!that.centerPoint) {
74 | that.prompt.update(evt.endPosition, "单击开始绘制");
75 |
76 | return;
77 | }
78 | that.state = "creating";
79 | that.prompt.update(evt.endPosition, "再次单击结束");
80 | let cartesian = that.getCatesian3FromPX(evt.endPosition, that.viewer);
81 | if (!cartesian) return;
82 | if (that.floatPoint) {
83 | that.floatPoint.position.setValue(cartesian);
84 | that.floatPosition = cartesian.clone();
85 | }
86 | that.radius = Cesium.Cartesian3.distance(cartesian, that.position);
87 | }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
88 | }
89 |
90 | /**
91 | * 通过坐标数组构建
92 | * @param {Array} lnglatArr 经纬度坐标数组
93 | * @callback {Function} callback 绘制成功后回调函数
94 | */
95 | createByPositions(lnglatArr, callback) {
96 | if (!lnglatArr || lnglatArr.length < 1) return;
97 | this.state = "startCreate";
98 | if (Array.isArray(lnglatArr)) {
99 | // 第一种 传入中间点坐标和边界上某点坐标
100 | let isCartesian3 = lnglatArr[0] instanceof Cesium.Cartesian3;
101 | let positions = [];
102 | if (isCartesian3) {
103 | positions = lnglatArr;
104 | } else {
105 | positions = util.lnglatsToCartesians(lnglatArr);
106 | }
107 | if (!positions || positions.length < 1) return;
108 | this.position = positions[0].clone();
109 | this.radius = Cesium.Cartesian3.distance(this.position, positions[1]);
110 | this.floatPosition = positions[1].clone();
111 | } else {
112 | // 第二种 传入中间点坐标和半径
113 | this.position = lnglatArr.position;
114 | this.radius = lnglatArr.radius;
115 | this.floatPosition = util.getPositionByLength();
116 | }
117 | this.centerPoint = this.createPoint(this.position);
118 | this.centerPoint.typeAttr = "center";
119 | this.floatPoint = this.createPoint(this.float);
120 | this.floatPoint.typeAttr = "float";
121 | this.entity = this.createCircle(this.position, this.radius);
122 | this.state = "endCreate";
123 | if (callback) callback(this.entity);
124 | }
125 |
126 | /**
127 | * 开始编辑
128 | * @param {Function} callback 回调函数
129 | */
130 | startEdit(callback) {
131 | if (this.state == "startEdit" || this.state == "editing" || !this.entity)
132 | return;
133 | this.state = "startEdit";
134 | if (!this.modifyHandler)
135 | this.modifyHandler = new Cesium.ScreenSpaceEventHandler(
136 | this.viewer.scene.canvas
137 | );
138 | let that = this;
139 | if (that.floatPoint) that.floatPoint.show = true;
140 | if (that.centerPoint) that.centerPoint.show = true;
141 | this.modifyHandler.setInputAction(function (evt) {
142 | if (!that.entity) return;
143 | that.state = "editing";
144 | let pick = that.viewer.scene.pick(evt.position);
145 | if (Cesium.defined(pick) && pick.id) {
146 | if (!pick.id.objId) that.modifyPoint = pick.id;
147 | that.forbidDrawWorld(true);
148 | } else {
149 | if (that.floatPoint) that.floatPoint.show = false;
150 | if (that.centerPoint) that.centerPoint.show = false;
151 | if (that.modifyHandler) {
152 | that.modifyHandler.destroy();
153 | that.modifyHandler = null;
154 |
155 | }
156 | }
157 | }, Cesium.ScreenSpaceEventType.LEFT_DOWN);
158 | this.modifyHandler.setInputAction(function (evt) {
159 | if (!that.modifyPoint) return;
160 | let cartesian = that.getCatesian3FromPX(evt.endPosition, that.viewer);
161 | if (!cartesian) return;
162 | that.state = "editing";
163 | if (that.modifyPoint.typeAttr == "center") {
164 | // 计算当前偏移量
165 | let subtract = Cesium.Cartesian3.subtract(
166 | cartesian,
167 | that.position,
168 | new Cesium.Cartesian3()
169 | );
170 | that.position = cartesian;
171 | that.centerPoint.position.setValue(that.position);
172 | that.entity.position.setValue(that.position);
173 |
174 | that.floatPosition = Cesium.Cartesian3.add(
175 | that.floatPosition,
176 | subtract,
177 | new Cesium.Cartesian3()
178 | );
179 | that.floatPoint.position.setValue(that.floatPosition);
180 | } else {
181 | that.floatPosition = cartesian;
182 | that.floatPoint.position.setValue(that.floatPosition);
183 | that.radius = Cesium.Cartesian3.distance(that.floatPosition, that.position);
184 | }
185 | if (callback) callback();
186 | }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
187 |
188 | this.modifyHandler.setInputAction(function (evt) {
189 | if (!that.modifyPoint) return;
190 | that.modifyPoint = null;
191 | that.forbidDrawWorld(false);
192 | that.state = "editing";
193 | }, Cesium.ScreenSpaceEventType.LEFT_UP);
194 | }
195 |
196 | /**
197 | * 结束绘制cartesiansToLnglats
198 | * @param {Function} callback 结束绘制后回调函数
199 | */
200 | endCreate() {
201 | let that = this;
202 | that.state = "endCreate";
203 | if (that.handler) {
204 | that.handler.destroy();
205 | that.handler = null;
206 | }
207 | if (that.floatPoint) that.floatPoint.show = false;
208 | if (that.centerPoint) that.centerPoint.show = false;
209 | if (that.prompt) {
210 | that.prompt.destroy();
211 | that.prompt = null;
212 | }
213 | }
214 |
215 | /**
216 | * 当前步骤结束
217 | */
218 | done() {
219 | if (this.state == "startCreate") {
220 | this.destroy();
221 | } else if (this.state == "creating") {
222 | this.destroy();
223 | } else if (this.state == "startEdit" || this.state == "editing") {
224 | this.endEdit();
225 | } else {
226 |
227 | }
228 | }
229 |
230 | /**
231 | * 结束编辑
232 | * @param {Function} callback 回调函数
233 | */
234 | endEdit(callback) {
235 | if (this.floatPoint) this.floatPoint.show = false;
236 | if (this.centerPoint) this.centerPoint.show = false;
237 | if (this.modifyHandler) {
238 | this.modifyHandler.destroy();
239 | this.modifyHandler = null;
240 | if (callback) callback(this.entity);
241 | }
242 | this.forbidDrawWorld(false);
243 | this.state = "endEdit";
244 | }
245 |
246 | createCircle() {
247 | let that = this;
248 | let defauteObj = {
249 | semiMajorAxis: new Cesium.CallbackProperty(function () {
250 | return that.radius;
251 | }, false),
252 | semiMinorAxis: new Cesium.CallbackProperty(function () {
253 | return that.radius;
254 | }, false),
255 | material:
256 | this.style.color instanceof Cesium.Color
257 | ? this.style.color
258 | : this.style.color
259 | ? Cesium.Color.fromCssColorString(this.style.color).withAlpha(
260 | this.style.colorAlpha || 1
261 | )
262 | : Cesium.Color.WHITE,
263 | outlineColor:
264 | this.style.outlineColor instanceof Cesium.Color
265 | ? this.style.outlineColor
266 | : this.style.outlineColor
267 | ? Cesium.Color.fromCssColorString(this.style.outlineColor).withAlpha(
268 | this.style.outlineColorAlpha || 1
269 | )
270 | : Cesium.Color.BLACK,
271 | outline: this.style.outline,
272 | heightReference : this.style.heightReference,
273 | outlineWidth: this.style.outlineWidth,
274 | fill: this.style.fill,
275 | };
276 | /* if (
277 | !this.style.heightReference ||
278 | Number(this.style.heightReference) == 0
279 | ) {
280 | defauteObj.height = 100 || this.style.height;
281 | defauteObj.heightReference = 0;
282 | } else {
283 | defauteObj.heightReference = 1;
284 | } */
285 | let ellipse = this.viewer.entities.add({
286 | position: this.position,
287 | ellipse: defauteObj,
288 | });
289 | ellipse.objId = this.objId;
290 | return ellipse;
291 | }
292 | setStyle(style) {
293 | if (!style) return;
294 | let color = Cesium.Color.fromCssColorString(style.color || "#ffff00");
295 | color = color.withAlpha(style.colorAlpha);
296 | this.entity.ellipse.material = color;
297 | this.entity.ellipse.outline = style.outline;
298 | this.entity.ellipse.outlineWidth = style.outlineWidth;
299 |
300 | let outlineColor = Cesium.Color.fromCssColorString(
301 | style.outlineColor || "#000000"
302 | );
303 | outlineColor = outlineColor.withAlpha(style.outlineColorAlpha);
304 | this.entity.ellipse.outlineColor = outlineColor;
305 |
306 | this.entity.ellipse.heightReference = Number(style.heightReference);
307 | if (style.heightReference == 0) {
308 | this.entity.ellipse.height = Number(style.height);
309 | this.updatePointHeight(style.height);
310 | }
311 | this.entity.ellipse.fill = Boolean(style.fill);
312 | this.style = Object.assign(this.style, style);
313 | }
314 | getStyle() {
315 | let obj = {};
316 | let ellipse = this.entity.ellipse;
317 | let color = ellipse.material.color.getValue();
318 | obj.colorAlpha = color.alpha;
319 | obj.color = new Cesium.Color(
320 | color.red,
321 | color.green,
322 | color.blue,
323 | 1
324 | ).toCssHexString();
325 | if (ellipse.outline) obj.outline = ellipse.outline.getValue();
326 | obj.outlineWidth = ellipse.outlineWidth._value;
327 | let outlineColor = ellipse.outlineColor.getValue();
328 | obj.outlineColorAlpha = outlineColor.alpha;
329 | obj.outlineColor = new Cesium.Color(
330 | outlineColor.red,
331 | outlineColor.green,
332 | outlineColor.blue,
333 | 1
334 | ).toCssHexString();
335 | if (ellipse.height) obj.height = ellipse.height.getValue();
336 | if (ellipse.fill) obj.fill = ellipse.fill.getValue();
337 | obj.heightReference = ellipse.heightReference.getValue();
338 | return obj;
339 | }
340 |
341 | destroy() {
342 | if (this.handler) {
343 | this.handler.destroy();
344 | this.handler = null;
345 | }
346 | if (this.modifyHandler) {
347 | this.modifyHandler.destroy();
348 | this.modifyHandler = null;
349 | }
350 | if (this.entity) {
351 | this.viewer.entities.remove(this.entity);
352 | this.entity = null;
353 | }
354 | if (this.floatPoint) {
355 | this.viewer.entities.remove(this.floatPoint);
356 | this.floatPoint = null;
357 | }
358 | if (this.centerPoint) {
359 | this.viewer.entities.remove(this.centerPoint);
360 | this.centerPoint = null;
361 | }
362 |
363 | this.style = null;
364 | this.modifyPoint = null;
365 | if (this.prompt) this.prompt.destroy();
366 | this.forbidDrawWorld(false);
367 | this.state = "no";
368 | }
369 | // 修改点的高度
370 | updatePointHeight(h) {
371 | let centerP = this.centerPoint.position.getValue();
372 | let floatP = this.floatPoint.position.getValue();
373 | centerP = util.updatePositionsHeight(
374 | [centerP],
375 | Number(this.style.height)
376 | )[0];
377 | floatP = util.updatePositionsHeight(
378 | [floatP],
379 | Number(this.style.height)
380 | )[0];
381 |
382 | this.centerPoint.position.setValue(centerP);
383 | this.floatPoint.position.setValue(floatP);
384 | }
385 | getPositions(isWgs84) {
386 | let positions = [];
387 | if (isWgs84) {
388 | positions = util.cartesiansToLnglats([this.position, this.floatPosition],this.viewer);
389 | } else {
390 | positions = [this.position, this.floatPosition];
391 | }
392 | return positions;
393 | }
394 |
395 | }
396 |
397 | export default CreateCircle;
--------------------------------------------------------------------------------
/src/js/plot/drawTool.js:
--------------------------------------------------------------------------------
1 |
2 | import CreateBillboard from './createBillboard.js'
3 | import CreateCircle from './createCircle.js'
4 | import CreateGltfModel from './createGltfModel.js'
5 | import CreateLabel from './createLabel.js'
6 | import CreatePoint from './createPoint.js'
7 | import CreatePolygon from './createPolygon.js'
8 | import CreateRectangle from './createRectangle'
9 | import CreatePolyline from './createPolyline.js'
10 | /* import CreateArrow from "./createArrow"; */ // 军事标绘
11 | import util from '../util.js'
12 | /**
13 | * 绘制控制类
14 | *
15 | * @class
16 | * @example
17 | * let drawTool = new easy3d.DrawTool(window.viewer, {
18 | canEdit: true,
19 | });
20 | plotDrawTool.on("endCreate", function (entObj, ent) {});
21 | plotDrawTool.start({
22 | "name": "面",
23 | "type": "polygon",
24 | "style": {
25 | "color": "#0000ff",
26 | "outline": true,
27 | "outlineColor": "#ff0000",
28 | "heightReference": 1
29 | }
30 | })
31 | */
32 | class DrawTool {
33 | /**
34 | *
35 | * @param {Cesium.viewer} viewer 地图viewer对象
36 | * @param {Object} obj 相关属性配置
37 | * @param {Boolean} obj.canEdit 是否可编辑
38 | */
39 | constructor(viewer, obj) {
40 | if (!viewer) {
41 | console.warn("缺少必要参数!--viewer");
42 | return;
43 | }
44 | obj = obj || {};
45 | this.viewer = viewer;
46 | /**
47 | *
48 | * @property {Array} entityObjArr 标绘对象数组
49 | */
50 | this.entityObjArr = [];
51 | this.handler = null;
52 | this.removeHandler = new Cesium.ScreenSpaceEventHandler(
53 | this.viewer.scene.canvas
54 | );
55 | /* this.show = obj.drawEndShow == undefined ? true : obj.drawEndShow; */
56 |
57 | /**
58 | * @property {Object} nowEditEntityObj 当前编辑对象
59 | */
60 | this.startEditFun = null;
61 | this.endEditFun = null;
62 | this.removeFun = null;
63 | this.editingFun = undefined;
64 |
65 | this.deleteEntityObj = null;
66 |
67 | // 无论如何 进来先监听点击修改 与 右键删除事件 通过控制canEdit来判断要不要向下执行
68 | this.bindEdit();
69 | this.bindRemove();
70 |
71 | /**
72 | * @property {Boolear} canEdit 绘制的对象,是否可编辑
73 | */
74 | this.canEdit = obj.canEdit == undefined ? true : obj.canEdit;; // 是否可以编辑
75 |
76 | /**
77 | * @property {Boolear} fireEdit 绘制的对象,是否直接进入编辑状态(需要canEdit==true)
78 | */
79 | this.fireEdit = obj.fireEdit == undefined ? true : obj.fireEdit;;
80 |
81 | this.nowDrawEntityObj = null; // 当前绘制的对象
82 | this.nowEditEntityObj = null; // 当前编辑的对象
83 | }
84 |
85 | /**
86 | * 事件绑定
87 | * @param {String} type 事件类型(startEdit 开始编辑时 / endEdit 编辑结束时 / remove 删除对象时 / endCreate 创建完成后)
88 | * @param {Function} fun 绑定函数
89 | */
90 | on(type, fun) {
91 | if (type == "startEdit") {
92 | // 开始编辑事件
93 | this.startEditFun = fun;
94 | }
95 | if (type == "endEdit") {
96 | // 结束编辑事件
97 | this.endEditFun = fun;
98 | }
99 | if (type == "remove") {
100 | // 移除事件
101 | this.removeFun = fun;
102 | }
103 | if (type == "endCreate") {
104 | // 绘制完成事件
105 | this.endCreateFun = fun;
106 | }
107 | if (type == "editing") {
108 | // 正在编辑
109 | this.editingFun = fun;
110 | }
111 | }
112 |
113 | /**
114 | * 开启编辑功能
115 | */
116 | openEdit() {
117 | this.canEdit = true;
118 | }
119 |
120 | /**
121 | * 关闭编辑功能
122 | */
123 | closeEdit() {
124 | this.endEdit();
125 | this.canEdit = false;
126 | }
127 |
128 | /**
129 | * 开始绘制
130 | * @param {Object} opt 相关属性
131 | * @param {String} opt.type 绘制类型 polyline、polygon、billboard、circle、rectangle、gltfModel、point、label、arrow
132 | * @param {Object} opt.style 当前绘制对象的样式配置,具体配置见{@link style};
133 | * @returns {Object} entityObj 当前绘制对象
134 | */
135 | start(opt) {
136 | if (!opt || !opt.type) {
137 | return;
138 | }
139 | opt.id = opt.id || Number((new Date()).getTime() + "" + Number(Math.random() * 1000).toFixed(0));
140 | let that = this;
141 | this.endEdit(); // 绘制前 结束编辑
142 |
143 | if (this.nowDrawEntityObj && (
144 | this.nowDrawEntityObj.state == "startCreate" ||
145 | this.nowDrawEntityObj.state == "creating")) { // 禁止一次绘制多个
146 | this.nowDrawEntityObj.destroy();
147 | this.nowDrawEntityObj = null;
148 | }
149 | let entityObj = this.createByType(opt);
150 | if (!entityObj) return;
151 | entityObj.attr = opt || {}; // 保存开始绘制时的属性
152 |
153 | // 开始绘制
154 | entityObj.start(function (entity) {
155 | // 绘制完成后
156 | that.nowDrawEntityObj = undefined;
157 | that.entityObjArr.push(entityObj);
158 | // endCreateFun 和 success 无本质区别,若构建时 两个都设置了 当心重复
159 | if (opt.success) opt.success(entityObj, entity);
160 | if (that.endCreateFun) that.endCreateFun(entityObj, entity);
161 |
162 | if (opt.show == false) entityObj.setVisible(false);
163 |
164 | // 如果可以编辑 则绘制完成打开编辑
165 | if (that.canEdit && that.fireEdit) {
166 | entityObj.startEdit(function () {
167 | if (that.editingFun) that.editingFun(entityObj, entityObj.entity);
168 | });
169 | that.nowEditEntityObj = entityObj;
170 | if (that.startEditFun) that.startEditFun(entityObj, entity);
171 | }
172 | });
173 |
174 | this.nowDrawEntityObj = entityObj;
175 | return entityObj;
176 | }
177 |
178 | /**
179 | * 结束当前操作
180 | */
181 | end() {
182 | if (this.nowDrawEntityObj) {
183 |
184 | }
185 | }
186 |
187 | /**
188 | * 开始编辑绘制对象
189 | * @param {Object} entityObj 绘制的对象
190 | */
191 | startEditOne(entityObj) {
192 | if (!this.canEdit) return;
193 | if (this.nowEditEntityObj) {
194 | // 结束除当前选中实体的所有编辑操作
195 | this.nowEditEntityObj.endEdit();
196 | if (this.endEditFun) {
197 | this.endEditFun(this.nowEditEntityObj, this.nowEditEntityObj.getEntity()); // 结束事件
198 | }
199 | this.nowEditEntityObj = null;
200 | }
201 | let that = this;
202 | if (entityObj) {
203 | entityObj.startEdit(function () {
204 | if (that.editingFun) that.editingFun(entityObj, entityObj.entity);
205 | });
206 | if (this.startEditFun)
207 | this.startEditFun(entityObj, entityObj.getEntity());
208 | this.nowEditEntityObj = entityObj;
209 | }
210 | }
211 |
212 | /**
213 | * 修改绘制对象的样式
214 | * @param {Object} entityObj 绘制的对象
215 | * @param {Object} style 样式
216 | */
217 | updateOneStyle(entityObj, style) {
218 | if (entityObj) {
219 | entityObj.setStyle(style);
220 | }
221 | }
222 |
223 | /**
224 | * 根据坐标构建绘制对象
225 | * @param {Object} opt 绘制的对象
226 | * @param {Cesium.Cartesian3[] | Array} opt.positions 坐标数组
227 | * @param {Object} opt.style 当前绘制对象的样式配置,具体配置见{@link style};
228 | * @param {Funtion} opt.success 创建完成的回调函数
229 | * @param {Boolean} [opt.show] 创建完成后,是否展示
230 | */
231 | createByPositions(opt) {
232 | opt = opt || {};
233 | if (!opt) opt = {};
234 | if (!opt.positions) return;
235 | opt.id = opt.id || Number((new Date()).getTime() + "" + Number(Math.random() * 1000).toFixed(0));
236 | let that = this;
237 | let entityObj = this.createByType(opt);
238 | if (!entityObj) return;
239 | entityObj.attr = opt; // 保存开始绘制时的属性
240 | entityObj.createByPositions(opt.positions, function (entity) {
241 | that.entityObjArr.push(entityObj);
242 | entityObj.setStyle(opt.style); // 设置相关样式
243 | // endCreateFun 和 success 无本质区别,若构建时 两个都设置了 当心重复
244 | if (opt.success) opt.success(entityObj, entity);
245 | if (that.endCreateFun) that.endCreateFun(entityObj, entity);
246 | if (opt.show == false) entityObj.setVisible(false);
247 | // 如果可以编辑 则绘制完成打开编辑
248 | if (that.canEdit && that.fireEdit) {
249 | entityObj.startEdit(function () {
250 | if (that.editingFun) that.editingFun(entityObj, entityObj.entity);
251 | });
252 | if (that.startEditFun) that.startEditFun(entityObj, entity);
253 | that.nowEditEntityObj = entityObj;
254 | }
255 | });
256 | return entityObj;
257 | }
258 |
259 | /**
260 | * 由geojson格式数据创建对象
261 | * @param {Object} data geojson格式数据
262 | */
263 | createByGeojson(data) {
264 | let { features } = data;
265 | let entObjArr = [];
266 | for (let i = 0; i < features.length; i++) {
267 | let feature = features[i];
268 | const { properties, geometry } = feature;
269 | let plotType = properties.plotType;
270 | const geoType = geometry.type;
271 | const coordinates = geometry.coordinates;
272 | let positions = [];
273 | let drawType = "";
274 | switch (geoType) {
275 | case "LineString":
276 | positions = util.lnglatsToCartesians(coordinates);
277 | drawType = "polyline";
278 | break;
279 | case "Polygon":
280 | positions = util.lnglatsToCartesians(coordinates[0]);
281 | drawType = "polygon";
282 | break;
283 | case "Point":
284 | positions = util.lnglatsToCartesians([coordinates])[0];
285 | drawType = plotType;
286 | break;
287 | default: ;
288 | }
289 | this.fireEdit = false;
290 | let entObj = this.createByPositions({
291 | type: drawType,
292 | styleType: plotType,
293 | positions: positions,
294 | style: properties.style
295 | })
296 | if (entObj) entObjArr.push(entObj);
297 | }
298 | return entObjArr;
299 | }
300 |
301 | /**
302 | * 转为geojson格式
303 | * @returns {Object} featureCollection geojson格式数据
304 | */
305 | toGeojson() {
306 | let featureCollection = {
307 | type: "FeatureCollection",
308 | features: [],
309 | };
310 | if (this.entityObjArr.length == 0) return null;
311 | for (let i = 0; i < this.entityObjArr.length; i++) {
312 | let item = this.entityObjArr[i];
313 | let lnglats = item.getPositions(true);
314 | // geojson中 单个坐标 不含高度 否则geojsondatasourece加载会有问题
315 | let coordinates = [];
316 | for (let step = 0; step < lnglats.length; step++) {
317 | coordinates.push([lnglats[step][0], lnglats[step][1]])
318 | }
319 | let style = item.getStyle();
320 | let geoType = this.transType(item.type);
321 | let feature = {
322 | "type": "Feature",
323 | "properties": {
324 | "plotType": item.type,
325 | "style": style,
326 | },
327 | "geometry": {
328 | "type": geoType,
329 | "coordinates": []
330 | }
331 | }
332 | switch (geoType) {
333 | case "Polygon":
334 | feature.geometry.coordinates = [coordinates];
335 | break;
336 | case "Point":
337 | feature.geometry.coordinates = coordinates;
338 | break;
339 | case "LineString":
340 | feature.geometry.coordinates = coordinates;
341 | break;
342 | case "":
343 |
344 | default: ;
345 | }
346 | feature.properties = Object.assign(feature.properties, item.properties);
347 | featureCollection.features.push(feature);
348 | }
349 | return featureCollection;
350 | }
351 |
352 | // 标绘类型和geojson数据类型相互转换
353 | transType(plotType) {
354 | let geoType = '';
355 | switch (plotType) {
356 | case "polyline":
357 | geoType = "LineString";
358 | break;
359 | case "polygon":
360 | geoType = "Polygon";
361 | break;
362 | case "point":
363 | case "gltfModel":
364 | case "label":
365 | case "billboard":
366 | geoType = "Point";
367 | break;
368 | default:
369 | geoType = plotType;
370 | }
371 | return geoType;
372 | }
373 |
374 | /**
375 | * 销毁
376 | */
377 | destroy() {
378 | // 取消当前绘制
379 | if (this.nowEditEntityObj) {
380 | this.nowEditEntityObj.destroy();
381 | this.nowEditEntityObj = null;
382 | }
383 | if (this.nowDrawEntityObj) {
384 | this.nowDrawEntityObj.destroy();
385 | this.nowDrawEntityObj = null;
386 | }
387 |
388 | for (let i = 0; i < this.entityObjArr.length; i++) {
389 | this.entityObjArr[i].destroy();
390 | }
391 | this.entityObjArr = [];
392 | this.nowEditEntityObj = null;
393 |
394 | if (this.handler) {
395 | this.handler.destroy();
396 | this.handler = null;
397 | }
398 |
399 | if (this.removeHandler) {
400 | this.removeHandler.destroy();
401 | this.removeHandler = null;
402 | }
403 | }
404 |
405 | /**
406 | * 移除某个绘制对象
407 | * @param {Object} entityObj 已绘制完成绘制对象
408 | */
409 | removeOne(entityObj) {
410 | if (!entityObj) return;
411 | if (!entityObj) return;
412 | if (entityObj.state != "endCreate" || entityObj.state != "endEdit") {
413 | entityObj.destroy();
414 | } else {
415 | this.removeByObjId(entityObj.objId);
416 | }
417 |
418 | }
419 |
420 | /**
421 | * 移除全部绘制对象
422 | */
423 | removeAll() {
424 | // 取消当前绘制
425 | if (this.nowDrawEntityObj) {
426 | this.nowDrawEntityObj.destroy();
427 | this.nowDrawEntityObj = null;
428 | }
429 |
430 | if (this.nowEditEntityObj) {
431 | this.nowEditEntityObj.destroy();
432 | this.nowEditEntityObj = null;
433 | }
434 |
435 | for (let i = 0; i < this.entityObjArr.length; i++) {
436 | let obj = this.entityObjArr[i];
437 | obj.destroy();
438 | }
439 | this.entityObjArr = [];
440 | this.nowEditEntityObj = null;
441 | }
442 |
443 | /**
444 | * 是否包含某个对象
445 | * @param {Object} entityObj 绘制对象
446 | */
447 | hasEntityObj(entityObj) {
448 | if (!entityObj) return false;
449 | let obj = this.getEntityObjByObjId(entityObj.objId);
450 | return obj != {} ? true : false;
451 | }
452 |
453 | /**
454 | * 根据id移除创建的对象
455 | * @param {String | Number} id 对象id
456 | */
457 | removeByObjId(id) {
458 | let obj = this.getEntityObjByObjId(id);
459 | this.entityObjArr.splice(obj.index, 1);
460 | // 触发on绑定的移除事件
461 | if (this.removeFun)
462 | this.removeFun(obj.entityObj, obj.entityObj.getEntity());
463 | if (obj.entityObj) {
464 | obj.entityObj.destroy();
465 | }
466 | }
467 |
468 | /**
469 | * 根据attr.id移除创建的对象
470 | * @param {String | Number} id 创建时的attr.id
471 | */
472 | removeById(id) {
473 | let obj = this.getEntityObjById(id);
474 | this.entityObjArr.splice(obj.index, 1);
475 | // 触发on绑定的移除事件
476 | if (this.removeFun)
477 | this.removeFun(obj.entityObj, obj.entityObj.getEntity());
478 | if (obj.entityObj) {
479 | obj.entityObj.destroy();
480 | }
481 | }
482 |
483 |
484 | /**
485 | * 根据id缩放至绘制的对象
486 | * @param {String} id 对象id
487 | */
488 | zoomToByObjId(id) {
489 | let obj = this.getEntityObjByObjId(id);
490 | if (obj.entityObj) {
491 | obj.entityObj.zoomTo();
492 | }
493 | }
494 |
495 |
496 | /**
497 | * 根据attr属性字段获取对象
498 | * @param {String} fieldName 属性字段名称
499 | * @param {String} [fieldValue] 属性值,若不填,则默认以id进行查询
500 | * @returns {Object} obj 对象在数组中位置以及对象
501 | */
502 |
503 | getEntityObjByField(fieldName, fieldValue) {
504 | let obj = {};
505 | if (!fieldValue) {
506 | // 如果缺少第二个参数 则默认以attr.id进行查询
507 | for (let i = 0; i < this.entityObjArr.length; i++) {
508 | let item = this.entityObjArr[i];
509 | if (item.attr.id == fieldName) {
510 | obj.entityObj = item;
511 | obj.index = i;
512 | break;
513 | }
514 | }
515 | } else {
516 | // 否则 以键值对的形式进行查询
517 | for (let ind = 0; ind < this.entityObjArr.length; ind++) {
518 | let item = this.entityObjArr[ind];
519 | if (item.attr[fieldName] == fieldValue) {
520 | obj.entityObj = item;
521 | obj.index = ind;
522 | break;
523 | }
524 | }
525 | }
526 | return obj;
527 | }
528 |
529 | /**
530 | * 根据id设置对象的显示隐藏
531 | * @param {String | Number} id 对象id
532 | * @param {Boolean} visible 是否展示
533 | */
534 | setVisible(id, visible) {
535 | let obj = this.getEntityObjByField("id", id);
536 | if (obj.entityObj) obj.entityObj.setVisible(visible);
537 | }
538 |
539 | /**
540 | * 根据id获取对象
541 | * @param {String | Number} id entityObj的objid
542 | * @returns {Object} obj 对象在数组中位置以及对象
543 | */
544 | getEntityObjByObjId(id) {
545 | if (!id) return;
546 | let obj = {};
547 | for (let i = 0; i < this.entityObjArr.length; i++) {
548 | let item = this.entityObjArr[i];
549 | if (item.objId == id) {
550 | obj.entityObj = item;
551 | obj.index = i;
552 | break;
553 | }
554 | }
555 | return obj;
556 | }
557 |
558 | /**
559 | * 根据id获取对象,同getEntityObjByField('id',idvalue);
560 | * @param {String | Number} id 创建时的attr中的id
561 | * @returns {Object} obj 对象在数组中位置以及对象
562 | */
563 | getEntityObjById(id) {
564 | if (!id) return;
565 | let obj = {};
566 | for (let i = 0; i < this.entityObjArr.length; i++) {
567 | let item = this.entityObjArr[i];
568 | if (item.attr.id == id) {
569 | obj.entityObj = item;
570 | obj.index = i;
571 | break;
572 | }
573 | }
574 | return obj;
575 | }
576 |
577 |
578 | // 绑定编辑
579 | bindEdit() {
580 | let that = this;
581 | // 如果是线 面 则需要先选中
582 | if (!this.handler) this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
583 | this.handler.setInputAction(function (evt) {
584 | if (!that.canEdit) return;
585 | // 若当前正在绘制 则无法进行编辑操作
586 | if (that.nowDrawEntityObj) return;
587 | let pick = that.viewer.scene.pick(evt.position);
588 | if (Cesium.defined(pick) && pick.id) { // 选中实体
589 | for (let i = 0; i < that.entityObjArr.length; i++) {
590 | if (
591 | pick.id.objId == that.entityObjArr[i].objId &&
592 | (that.entityObjArr[i].state != "startCreate" ||
593 | that.entityObjArr[i].state != "creating" ||
594 | that.entityObjArr[i].state != "endEdit")
595 | ) {
596 | // 结束上一个实体的编辑操作
597 | if (that.nowEditEntityObj) {
598 | that.nowEditEntityObj.endEdit();
599 | if (that.endEditFun) {
600 | that.endEditFun(
601 | that.nowEditEntityObj,
602 | that.nowEditEntityObj.getEntity()
603 | );
604 | }
605 | that.nowEditEntityObj = null;
606 | }
607 | // 开始当前实体的编辑
608 | that.entityObjArr[i].startEdit(function () {
609 | if (that.editingFun) that.editingFun(that.nowEditEntityObj, that.nowEditEntityObj.entity);
610 | });
611 | if (that.startEditFun) that.startEditFun(that.entityObjArr[i], pick.id); // 开始编辑
612 | that.nowEditEntityObj = that.entityObjArr[i];
613 | break;
614 | }
615 | }
616 | } else { // 未选中实体 则结束全部绘制
617 | if (that.nowEditEntityObj) {
618 | that.nowEditEntityObj.endEdit();
619 | if (that.endEditFun) {
620 | that.endEditFun(
621 | that.nowEditEntityObj,
622 | that.nowEditEntityObj.getEntity()
623 | );
624 | }
625 | that.nowEditEntityObj = undefined;
626 | }
627 | }
628 | }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
629 | }
630 |
631 | // 绑定右键删除
632 | bindRemove() {
633 | let that = this;
634 | // 如果是线 面 则需要先选中
635 | if (!this.handler) this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
636 | this.handler.setInputAction(function (evt) {
637 | if (!that.canEdit) return;
638 | // 若当前正在绘制 则无法进行删除
639 | if (that.nowDrawEntityObj) return;
640 | let pick = that.viewer.scene.pick(evt.position);
641 | if (!pick || !pick.id) return;
642 | /* let selectEntobj = undefined; */
643 | /* for (let i = 0; i < that.entityObjArr.length; i++) {
644 | if (pick.id.objId == that.entityObjArr[i].objId) {
645 | selectEntobj = that.entityObjArr[i];
646 | break;
647 | }
648 | } */
649 |
650 | that.createDelteDom(evt.position, pick.id.objId);
651 |
652 | }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
653 | }
654 |
655 | createDelteDom(px, objId) {
656 | if (!objId) return;
657 | let deleteDom = window.document.createElement("span");
658 | deleteDom.style.background = "rgba(0,0,0,0.5)";
659 | deleteDom.style.position = "absolute";
660 | deleteDom.style.color = "white";
661 | deleteDom.style.left = (px.x + 10) + "px";
662 | deleteDom.style.top = (px.y + 10) + "px";
663 | deleteDom.style.padding = "4px";
664 | deleteDom.style.cursor = "pointer";
665 | deleteDom.id = "easy3d-plot-delete";
666 | deleteDom.setAttribute("objId", objId);
667 | deleteDom.innerHTML = `删除`;
668 | let mapDom = window.document.getElementById(this.viewer.container.id);
669 | mapDom.appendChild(deleteDom);
670 |
671 | const clsBtn = window.document.getElementById("easy3d-plot-delete");
672 | if (!clsBtn) return;
673 | let that = this;
674 | clsBtn.addEventListener("click", (e) => {
675 | let id = deleteDom.getAttribute("objId");
676 | that.removeByObjId(id);
677 | });
678 | document.addEventListener("click", function () {
679 | clsBtn.remove();
680 | });
681 | }
682 |
683 | /**
684 | * 结束编辑
685 | */
686 | endEdit() {
687 | if (this.nowEditEntityObj) {
688 | // 结束除当前选中实体的所有编辑操作
689 | this.nowEditEntityObj.endEdit();
690 | if (this.endEditFun) {
691 | this.endEditFun(
692 | this.nowEditEntityObj,
693 | this.nowEditEntityObj.getEntity()
694 | ); // 结束事件
695 | }
696 | this.nowEditEntityObj = null;
697 | }
698 | for (let i = 0; i < this.entityObjArr.length; i++) {
699 | this.entityObjArr[i].endEdit();
700 | }
701 | }
702 |
703 | done() {
704 | if (this.nowEditEntityObj) {
705 | this.nowEditEntityObj.done();
706 | if (this.endEditFun) this.endEditFun(this.nowEditEntityObj, this.nowEditEntityObj.getEntity());
707 | this.nowEditEntityObj = undefined;
708 | }
709 |
710 | if (this.nowDrawEntityObj) {
711 | this.nowDrawEntityObj.done();
712 | this.entityObjArr.push(this.nowDrawEntityObj);
713 | if (this.endCreateFun) this.endCreateFun(this.nowDrawEntityObj, this.nowDrawEntityObj.getEntity());
714 | this.nowDrawEntityObj = undefined;
715 | }
716 | }
717 |
718 |
719 | /**
720 | * 获取当前所有对象
721 | * @returns {Array} entityObjArr
722 | */
723 | getEntityObjArr() {
724 | return this.entityObjArr;
725 | }
726 | createByType(opt) {
727 | let entityObj = undefined;
728 | let name = "";
729 | opt = opt || {};
730 | if (opt.type == "polyline") {
731 | entityObj = new CreatePolyline(this.viewer, opt.style);
732 | name = "折线_";
733 | }
734 |
735 | if (opt.type == "polygon") {
736 | entityObj = new CreatePolygon(this.viewer, opt.style);
737 | name = "面_";
738 | }
739 |
740 | if (opt.type == "billboard") {
741 | entityObj = new CreateBillboard(this.viewer, opt.style);
742 | name = "图标_";
743 | }
744 |
745 | if (opt.type == "circle") {
746 | entityObj = new CreateCircle(this.viewer, opt.style);
747 | name = "圆_";
748 | }
749 |
750 | if (opt.type == "rectangle") {
751 | entityObj = new CreateRectangle(this.viewer, opt.style);
752 | name = "矩形_";
753 | }
754 |
755 | if (opt.type == "gltfModel") {
756 | entityObj = new CreateGltfModel(this.viewer, opt.style);
757 | name = "模型_";
758 | }
759 |
760 | if (opt.type == "point") {
761 | entityObj = new CreatePoint(this.viewer, opt.style);
762 | name = "点_";
763 | }
764 | if (opt.type == "label") {
765 | entityObj = new CreateLabel(this.viewer, opt.style);
766 | name = "文字_";
767 | }
768 |
769 | // ========== 以下为付费功能 ==============
770 | // if (opt.type == "arrow") {
771 | // /**
772 | // * situationType值及对应的类型:
773 | // * 1-攻击箭头 2-攻击箭头(平尾)3-攻击箭头(燕尾)4-闭合曲面 5-钳击箭头
774 | // * 6-单尖直箭头 7-粗单尖直箭头(带燕尾) 8-集结地 9-弓形面 10-直箭头
775 | // * 11-矩形旗 12-扇形 13-三角旗 14-矩形波浪旗 17-多边形 18-圆形
776 | // */
777 | // if (!opt.arrowType) {
778 | // console.log("缺少军事标绘类型");
779 | // return;
780 | // }
781 | // entityObj = new CreateArrow(this.viewer, opt.arrowType, opt.style);
782 | // }
783 |
784 | if (entityObj) entityObj.name = name + new Date().getTime();
785 | return entityObj;
786 | }
787 | }
788 |
789 | export default DrawTool;
790 |
791 |
792 |
--------------------------------------------------------------------------------