cesium-xs-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,370 @@
1
+ import { Config } from '../core/Config.js';
2
+ import { DrawTool, DrawType } from './tools/DrawTool.js';
3
+ import { EditTool, EditMode } from './tools/EditTool.js';
4
+ import { SelectTool, SelectType } from './tools/SelectTool.js';
5
+ import { DeleteTool, DeleteMode } from './tools/DeleteTool.js';
6
+
7
+ /**
8
+ * 工具名称枚举
9
+ */
10
+ export const ToolName = {
11
+ DRAW: 'draw',
12
+ EDIT: 'edit',
13
+ SELECT: 'select',
14
+ DELETE: 'delete'
15
+ };
16
+
17
+ /**
18
+ * 编辑器模块
19
+ * 负责工具的注册、切换、激活管理
20
+ */
21
+ export class EditorModule {
22
+ constructor(viewer, options = {}) {
23
+ this.viewer = viewer;
24
+ this.config = Config.getInstance().getConfig();
25
+ this.graphicModule = null; // 依赖注入
26
+ this.eventModule = null; // 依赖注入
27
+ this.toolMap = new Map(); // 工具缓存
28
+ this.currentTool = null; // 当前激活的工具
29
+ this.currentToolName = null;
30
+ this._isActive = false;
31
+ }
32
+
33
+ /**
34
+ * 设置依赖模块
35
+ * @param {object} deps 依赖模块 { graphicModule, eventModule }
36
+ */
37
+ setDependencies(deps) {
38
+ this.graphicModule = deps.graphicModule;
39
+ this.eventModule = deps.eventModule;
40
+ return this;
41
+ }
42
+
43
+ /**
44
+ * 初始化编辑器,注册所有内置工具
45
+ */
46
+ init() {
47
+ // 注册内置工具
48
+ this.registerTool(ToolName.DRAW, new DrawTool(this));
49
+ this.registerTool(ToolName.EDIT, new EditTool(this));
50
+ this.registerTool(ToolName.SELECT, new SelectTool(this));
51
+ this.registerTool(ToolName.DELETE, new DeleteTool(this));
52
+
53
+ return this;
54
+ }
55
+
56
+ /**
57
+ * 注册工具
58
+ * @param {string} name 工具名称
59
+ * @param {BaseTool} tool 工具实例
60
+ * @returns {EditorModule}
61
+ */
62
+ registerTool(name, tool) {
63
+ this.toolMap.set(name, tool);
64
+ return this;
65
+ }
66
+
67
+ /**
68
+ * 获取工具
69
+ * @param {string} name 工具名称
70
+ * @returns {BaseTool|undefined}
71
+ */
72
+ getTool(name) {
73
+ return this.toolMap.get(name);
74
+ }
75
+
76
+ /**
77
+ * 获取当前激活的工具
78
+ * @returns {BaseTool|null}
79
+ */
80
+ getCurrentTool() {
81
+ return this.currentTool;
82
+ }
83
+
84
+ /**
85
+ * 获取当前激活的工具名称
86
+ * @returns {string|null}
87
+ */
88
+ getCurrentToolName() {
89
+ return this.currentToolName;
90
+ }
91
+
92
+ /**
93
+ * 激活指定工具
94
+ * @param {string} name 工具名称
95
+ * @returns {EditorModule}
96
+ */
97
+ activateTool(name) {
98
+ const tool = this.toolMap.get(name);
99
+ if (!tool) {
100
+ console.warn(`工具 ${name} 未注册`);
101
+ return this;
102
+ }
103
+
104
+ // 停用当前工具
105
+ if (this.currentTool) {
106
+ this.currentTool.deactivate();
107
+ }
108
+
109
+ // 激活新工具
110
+ this.currentTool = tool;
111
+ this.currentToolName = name;
112
+ this.currentTool.activate();
113
+ this._isActive = true;
114
+
115
+ console.log(`工具 ${name} 已激活`);
116
+ return this;
117
+ }
118
+
119
+ /**
120
+ * 停用当前工具
121
+ * @returns {EditorModule}
122
+ */
123
+ deactivateTool() {
124
+ if (this.currentTool) {
125
+ this.currentTool.deactivate();
126
+ }
127
+ this.currentTool = null;
128
+ this.currentToolName = null;
129
+ this._isActive = false;
130
+
131
+ return this;
132
+ }
133
+
134
+ /**
135
+ * 激活绘制工具并设置类型
136
+ * @param {string} type 绘制类型 DrawType
137
+ * @returns {EditorModule}
138
+ */
139
+ activateDrawTool(type = DrawType.POINT) {
140
+ const drawTool = this.toolMap.get(ToolName.DRAW);
141
+ if (drawTool) {
142
+ drawTool.setDrawType(type);
143
+ }
144
+ return this.activateTool(ToolName.DRAW);
145
+ }
146
+
147
+ /**
148
+ * 激活选择工具并设置类型
149
+ * @param {string} type 选择类型 SelectType
150
+ * @returns {EditorModule}
151
+ */
152
+ activateSelectTool(type = SelectType.POINT) {
153
+ const selectTool = this.toolMap.get(ToolName.SELECT);
154
+ if (selectTool) {
155
+ selectTool.setSelectType(type);
156
+ }
157
+ return this.activateTool(ToolName.SELECT);
158
+ }
159
+
160
+ /**
161
+ * 激活编辑工具并设置模式
162
+ * @param {string} mode 编辑模式 EditMode
163
+ * @returns {EditorModule}
164
+ */
165
+ activateEditTool(mode = EditMode.VERTEX) {
166
+ const editTool = this.toolMap.get(ToolName.EDIT);
167
+ if (editTool) {
168
+ editTool.setEditMode(mode);
169
+ }
170
+ return this.activateTool(ToolName.EDIT);
171
+ }
172
+
173
+ /**
174
+ * 激活删除工具并设置模式
175
+ * @param {string} mode 删除模式 DeleteMode
176
+ * @returns {EditorModule}
177
+ */
178
+ activateDeleteTool(mode = DeleteMode.SINGLE) {
179
+ const deleteTool = this.toolMap.get(ToolName.DELETE);
180
+ if (deleteTool) {
181
+ deleteTool.setDeleteMode(mode);
182
+ }
183
+ return this.activateTool(ToolName.DELETE);
184
+ }
185
+
186
+ /**
187
+ * 获取绘制工具(快捷方法)
188
+ * @returns {DrawTool}
189
+ */
190
+ getDrawTool() {
191
+ return this.toolMap.get(ToolName.DRAW);
192
+ }
193
+
194
+ /**
195
+ * 获取选择工具(快捷方法)
196
+ * @returns {SelectTool}
197
+ */
198
+ getSelectTool() {
199
+ return this.toolMap.get(ToolName.SELECT);
200
+ }
201
+
202
+ /**
203
+ * 获取编辑工具(快捷方法)
204
+ * @returns {EditTool}
205
+ */
206
+ getEditTool() {
207
+ return this.toolMap.get(ToolName.EDIT);
208
+ }
209
+
210
+ /**
211
+ * 获取删除工具(快捷方法)
212
+ * @returns {DeleteTool}
213
+ */
214
+ getDeleteTool() {
215
+ return this.toolMap.get(ToolName.DELETE);
216
+ }
217
+
218
+ // ========== 公开回调 setter ==========
219
+
220
+ /**
221
+ * 设置绘制完成回调
222
+ * @param {Function} callback
223
+ */
224
+ onDrawComplete(callback) {
225
+ this.getDrawTool()._options.onGraphicCreated = callback;
226
+ return this;
227
+ }
228
+
229
+ /**
230
+ * 设置绘制进度回调
231
+ * @param {Function} callback
232
+ */
233
+ onDrawProgress(callback) {
234
+ this.getDrawTool()._options.onDrawProgress = callback;
235
+ return this;
236
+ }
237
+
238
+ /**
239
+ * 设置绘制取消回调
240
+ * @param {Function} callback
241
+ */
242
+ onDrawCancel(callback) {
243
+ this.getDrawTool()._options.onDrawCancel = callback;
244
+ return this;
245
+ }
246
+
247
+ /**
248
+ * 设置编辑完成回调
249
+ * @param {Function} callback
250
+ */
251
+ onEditComplete(callback) {
252
+ this.getEditTool()._options.onEditComplete = callback;
253
+ return this;
254
+ }
255
+
256
+ /**
257
+ * 设置编辑选中回调
258
+ * @param {Function} callback
259
+ */
260
+ onEditSelect(callback) {
261
+ this.getEditTool()._options.onSelect = callback;
262
+ return this;
263
+ }
264
+
265
+ /**
266
+ * 设置选择回调
267
+ * @param {Function} callback
268
+ */
269
+ onSelect(callback) {
270
+ this.getSelectTool()._options.onSelect = callback;
271
+ return this;
272
+ }
273
+
274
+ /**
275
+ * 设置选择变化回调
276
+ * @param {Function} callback
277
+ */
278
+ onSelectionChange(callback) {
279
+ this.getSelectTool()._options.onSelectionChange = callback;
280
+ return this;
281
+ }
282
+
283
+ /**
284
+ * 设置框选完成回调
285
+ * @param {Function} callback
286
+ */
287
+ onSelectionComplete(callback) {
288
+ this.getSelectTool()._options.onSelectionComplete = callback;
289
+ return this;
290
+ }
291
+
292
+ /**
293
+ * 设置删除前回调
294
+ * @param {Function} callback
295
+ */
296
+ onBeforeDelete(callback) {
297
+ this.getDeleteTool()._options.onBeforeDelete = callback;
298
+ return this;
299
+ }
300
+
301
+ /**
302
+ * 设置删除完成回调
303
+ * @param {Function} callback
304
+ */
305
+ onDelete(callback) {
306
+ this.getDeleteTool()._options.onDelete = callback;
307
+ return this;
308
+ }
309
+
310
+ /**
311
+ * 设置批量删除完成回调
312
+ * @param {Function} callback
313
+ */
314
+ onDeleteMultiple(callback) {
315
+ this.getDeleteTool()._options.onDeleteMultiple = callback;
316
+ return this;
317
+ }
318
+
319
+ /**
320
+ * 获取所有已选择的图元 ID(需要先使用选择工具)
321
+ * @returns {string[]}
322
+ */
323
+ getSelectedIds() {
324
+ const selectTool = this.getSelectTool();
325
+ if (selectTool) {
326
+ return selectTool.getSelectedIds();
327
+ }
328
+ return [];
329
+ }
330
+
331
+ /**
332
+ * 删除所有已选择的图元
333
+ * @returns {object} { deleted, failed }
334
+ */
335
+ deleteSelected() {
336
+ const selectTool = this.getSelectTool();
337
+ if (selectTool) {
338
+ const ids = selectTool.getSelectedIds();
339
+ const deleteTool = this.getDeleteTool();
340
+ if (deleteTool) {
341
+ return deleteTool.deleteMultiple(ids);
342
+ }
343
+ }
344
+ return { deleted: [], failed: [] };
345
+ }
346
+
347
+ /**
348
+ * 检查编辑器是否处于激活状态
349
+ * @returns {boolean}
350
+ */
351
+ isActive() {
352
+ return this._isActive;
353
+ }
354
+
355
+ /**
356
+ * 销毁编辑器
357
+ */
358
+ destroy() {
359
+ this.deactivateTool();
360
+ this.toolMap.clear();
361
+ this.graphicModule = null;
362
+ this.eventModule = null;
363
+ }
364
+ }
365
+
366
+ // 导出工具类型枚举
367
+ export { DrawType } from './tools/DrawTool.js';
368
+ export { EditMode } from './tools/EditTool.js';
369
+ export { SelectType } from './tools/SelectTool.js';
370
+ export { DeleteMode } from './tools/DeleteTool.js';
@@ -0,0 +1,211 @@
1
+ import { Config } from '../core/Config.js';
2
+
3
+ /**
4
+ * 事件模块(对应平台 IEventModule)
5
+ * 职责:注册/注销鼠标、键盘、场景事件观察者
6
+ */
7
+ export class EventModule {
8
+ constructor(viewer) {
9
+ this.viewer = viewer;
10
+ this.config = Config.getInstance().getConfig();
11
+ this.observers = new Map(); // 观察者缓存(事件类型 -> 回调列表)
12
+ this.handlers = {}; // Cesium 事件处理器缓存
13
+ }
14
+
15
+ /**
16
+ * 注册事件观察者(对齐平台 Observer 模式)
17
+ * @param {string} eventType 事件类型
18
+ * - mouse:click/move/down/up
19
+ * - keyboard:keyup/keydown
20
+ * - scene:load/change/zoom
21
+ * @param {Function} callback 回调函数
22
+ * @returns {string} 观察者ID(用于注销)
23
+ * @example
24
+ * // 注册鼠标点击事件
25
+ * const observerId = eventModule.registerObserver('mouse:click', (position, entity) => {
26
+ * console.log('点击位置:', position, '点击实体:', entity);
27
+ * });
28
+ */
29
+ registerObserver(eventType, callback) {
30
+ const observerId = `obs-${Date.now()}-${Math.random().toString(36).slice(2)}`;
31
+ const [baseType, subType] = eventType.split(':');
32
+
33
+ // 初始化观察者列表
34
+ if (!this.observers.has(eventType)) {
35
+ this.observers.set(eventType, new Map());
36
+ // 创建 Cesium 事件处理器
37
+ this.createEventHandler(baseType, subType);
38
+ }
39
+
40
+ // 存储观察者
41
+ this.observers.get(eventType).set(observerId, callback);
42
+ return observerId;
43
+ }
44
+
45
+ /**
46
+ * 注销事件观察者
47
+ * @param {string} eventType 事件类型
48
+ * @param {string} observerId 观察者ID
49
+ * @returns {boolean} 是否注销成功
50
+ */
51
+ unregisterObserver(eventType, observerId) {
52
+ if (!this.observers.has(eventType)) return false;
53
+ const typeObservers = this.observers.get(eventType);
54
+ if (!typeObservers.has(observerId)) return false;
55
+
56
+ typeObservers.delete(observerId);
57
+ // 无观察者时销毁事件处理器
58
+ if (typeObservers.size === 0) {
59
+ this.destroyEventHandler(eventType);
60
+ this.observers.delete(eventType);
61
+ }
62
+ return true;
63
+ }
64
+
65
+ /**
66
+ * 注销所有事件观察者
67
+ */
68
+ unregisterAllObservers() {
69
+ // 按处理器类型安全销毁
70
+ Object.keys(this.handlers).forEach(eventKey => {
71
+ const handler = this.handlers[eventKey];
72
+ if (!handler) return;
73
+
74
+ // 判断事件类型:mouse 处理器是 ScreenSpaceEventHandler;
75
+ // scene 处理器是 addEventListener 返回的移除函数;
76
+ // keyboard 处理器是 document 事件监听器,需通过存储的引用移除。
77
+ const [baseType] = eventKey.split(':');
78
+ if (baseType === 'mouse' && typeof handler.destroy === 'function') {
79
+ handler.destroy();
80
+ } else if (baseType === 'scene' && typeof handler === 'function') {
81
+ handler();
82
+ } else if (baseType === 'keyboard') {
83
+ const subType = eventKey.split(':')[1];
84
+ document.removeEventListener(subType, handler);
85
+ }
86
+ });
87
+ this.handlers = {};
88
+ this.observers.clear();
89
+ }
90
+
91
+ // ========== 内部方法:创建 Cesium 事件处理器 ==========
92
+ createEventHandler(baseType, subType) {
93
+ const eventKey = `${baseType}:${subType}`;
94
+ if (this.handlers[eventKey]) return;
95
+
96
+ switch (baseType) {
97
+ // 鼠标事件
98
+ case 'mouse':
99
+ this.handlers[eventKey] = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
100
+ // 绑定鼠标事件
101
+ const mouseEventType = this.getCesiumMouseEventType(subType);
102
+ this.handlers[eventKey].setInputAction((movement) => {
103
+ // 获取点击位置和实体
104
+ const screenPos = movement.position || movement.endPosition;
105
+ if (!screenPos) return;
106
+
107
+ let position = null;
108
+ let lngLat = null;
109
+
110
+ try {
111
+ position = this.viewer.scene.pickPosition(screenPos);
112
+ } catch (e) {
113
+ // pickPosition 可能在无效情况下抛出异常
114
+ console.warn('获取点击位置失败:', e);
115
+ }
116
+
117
+ const pickedEntity = this.viewer.scene.pick(screenPos);
118
+
119
+ if (position) {
120
+ const cartographic = Cesium.Cartographic.fromCartesian(position);
121
+ lngLat = [
122
+ Cesium.Math.toDegrees(cartographic.longitude),
123
+ Cesium.Math.toDegrees(cartographic.latitude),
124
+ cartographic.height
125
+ ];
126
+ }
127
+
128
+ // 触发所有观察者回调
129
+ this.observers.get(eventKey).forEach(callback => {
130
+ callback(lngLat, pickedEntity ? pickedEntity.id : null, movement);
131
+ });
132
+ }, mouseEventType);
133
+ break;
134
+ // 场景事件
135
+ case 'scene':
136
+ if (subType === 'load') {
137
+ // 一次性加载事件:第一次 postRender 触发后自动移除
138
+ let removeListener = null;
139
+ const listener = () => {
140
+ if (removeListener) {
141
+ removeListener();
142
+ removeListener = null;
143
+ }
144
+ this.observers.get(eventKey)?.forEach(callback => callback());
145
+ };
146
+ removeListener = this.viewer.scene.postRender.addEventListener(listener);
147
+ this.handlers[eventKey] = removeListener;
148
+ } else if (subType === 'zoom') {
149
+ this.handlers[eventKey] = this.viewer.camera.changed.addEventListener(() => {
150
+ const zoomLevel = this.getZoomLevel();
151
+ this.observers.get(eventKey).forEach(callback => callback(zoomLevel));
152
+ });
153
+ }
154
+ break;
155
+ // 键盘事件
156
+ case 'keyboard':
157
+ this.handlers[eventKey] = (e) => {
158
+ if (e.type === subType) {
159
+ this.observers.get(eventKey).forEach(callback => callback(e));
160
+ }
161
+ };
162
+ document.addEventListener(subType, this.handlers[eventKey]);
163
+ break;
164
+ }
165
+ }
166
+
167
+ // ========== 内部方法:销毁事件处理器 ==========
168
+ destroyEventHandler(eventType) {
169
+ const [baseType, subType] = eventType.split(':');
170
+ const eventKey = eventType;
171
+
172
+ if (!this.handlers[eventKey]) return;
173
+ if (baseType === 'mouse') {
174
+ this.handlers[eventKey].destroy();
175
+ } else if (baseType === 'scene') {
176
+ // scene 事件返回的是移除函数
177
+ if (typeof this.handlers[eventKey] === 'function') {
178
+ this.handlers[eventKey]();
179
+ }
180
+ } else if (baseType === 'keyboard') {
181
+ document.removeEventListener(subType, this.handlers[eventKey]);
182
+ }
183
+ delete this.handlers[eventKey];
184
+ }
185
+
186
+ // ========== 工具方法:映射 Cesium 鼠标事件类型 ==========
187
+ getCesiumMouseEventType(subType) {
188
+ switch (subType) {
189
+ case 'click': return Cesium.ScreenSpaceEventType.LEFT_CLICK;
190
+ case 'rightclick': return Cesium.ScreenSpaceEventType.RIGHT_CLICK;
191
+ case 'move': return Cesium.ScreenSpaceEventType.MOUSE_MOVE;
192
+ case 'down': return Cesium.ScreenSpaceEventType.LEFT_DOWN;
193
+ case 'up': return Cesium.ScreenSpaceEventType.LEFT_UP;
194
+ default: return Cesium.ScreenSpaceEventType.LEFT_CLICK;
195
+ }
196
+ }
197
+
198
+ // ========== 工具方法:获取场景缩放级别 ==========
199
+ getZoomLevel() {
200
+ const camera = this.viewer.camera;
201
+ const positionCartographic = camera.positionCartographic;
202
+ const positionCartesian = Cesium.Cartesian3.fromRadians(
203
+ positionCartographic.longitude,
204
+ positionCartographic.latitude,
205
+ positionCartographic.height
206
+ );
207
+ const distance = Cesium.Cartesian3.distance(camera.position, positionCartesian);
208
+ // 映射为 0-20 级缩放(类似地图瓦片级别)
209
+ return Math.floor(Math.log2(6378137 * 2 * Math.PI / distance)) + 1;
210
+ }
211
+ }