@jorgmoritz/gis-manager 0.1.27 → 0.1.28

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.
@@ -1185,6 +1185,7 @@ var SceneManager = class {
1185
1185
  const tileset = await this.apply3Dtiles(url);
1186
1186
  if (tileset) {
1187
1187
  tileset._customId = layer_id;
1188
+ tileset._url = url;
1188
1189
  this.viewer.scene.primitives.add(tileset);
1189
1190
  console.log(`[SceneManager] \u65B0\u5EFA 3DTiles_id`, tileset._customId);
1190
1191
  }
@@ -1916,6 +1917,8 @@ var PathPreview = class {
1916
1917
  __publicField(this, "fovController");
1917
1918
  // FOV 控制器
1918
1919
  __publicField(this, "currentFOV");
1920
+ /** 已加载的 3D Tiles 实例(预览窗口独立的) */
1921
+ __publicField(this, "tilesets", /* @__PURE__ */ new Map());
1919
1922
  this.currentFOV = opts.fov ?? 50;
1920
1923
  this.ensureViewer();
1921
1924
  this.ensureFOVController();
@@ -1970,8 +1973,201 @@ var PathPreview = class {
1970
1973
  }
1971
1974
  } catch {
1972
1975
  }
1973
- v.scene;
1974
1976
  this.overlayViewer = v;
1977
+ this.syncFromMainViewer();
1978
+ this.disableUserInteraction();
1979
+ }
1980
+ /**
1981
+ * 从主 viewer 同步配置
1982
+ * 共享 Provider(安全)而非 DataSource
1983
+ */
1984
+ syncFromMainViewer() {
1985
+ if (!this.overlayViewer) return;
1986
+ this.syncImageryLayers();
1987
+ this.syncTerrainProvider();
1988
+ this.syncSceneSettings();
1989
+ this.sync3DTiles();
1990
+ }
1991
+ /**
1992
+ * 同步影像图层(共享 ImageryProvider)
1993
+ */
1994
+ syncImageryLayers() {
1995
+ const v = this.overlayViewer;
1996
+ if (!v) return;
1997
+ try {
1998
+ v.imageryLayers?.removeAll?.();
1999
+ const mainImageryLayers = this.mainViewer.imageryLayers;
2000
+ for (let i = 0; i < mainImageryLayers.length; i++) {
2001
+ const layer = mainImageryLayers.get(i);
2002
+ try {
2003
+ v.imageryLayers?.addImageryProvider?.(layer.imageryProvider);
2004
+ } catch {
2005
+ }
2006
+ }
2007
+ } catch (e) {
2008
+ console.warn("[PathPreview] Failed to sync imagery layers:", e);
2009
+ }
2010
+ }
2011
+ /**
2012
+ * 同步地形提供者(共享 TerrainProvider)
2013
+ */
2014
+ syncTerrainProvider() {
2015
+ const v = this.overlayViewer;
2016
+ if (!v) return;
2017
+ try {
2018
+ if (this.mainViewer.terrainProvider) {
2019
+ v.terrainProvider = this.mainViewer.terrainProvider;
2020
+ }
2021
+ } catch (e) {
2022
+ console.warn("[PathPreview] Failed to sync terrain provider:", e);
2023
+ }
2024
+ }
2025
+ /**
2026
+ * 同步 3D Tiles(点云、倾斜摄影等)
2027
+ * 注意:Primitive 不能直接共享,需要创建独立实例
2028
+ * 此方法查找主 viewer 中的 tileset 并尝试使用相同 URL 创建新实例
2029
+ */
2030
+ sync3DTiles() {
2031
+ const v = this.overlayViewer;
2032
+ if (!v) return;
2033
+ const C = this.CesiumNS;
2034
+ const mainPrimitives = this.mainViewer.scene.primitives;
2035
+ try {
2036
+ for (let i = 0; i < mainPrimitives.length; i++) {
2037
+ const primitive = mainPrimitives.get(i);
2038
+ if (primitive && primitive instanceof C.Cesium3DTileset) {
2039
+ const customId = primitive._customId;
2040
+ const url = primitive._url || primitive.resource?.url;
2041
+ if (url && customId && !this.tilesets.has(customId)) {
2042
+ this.add3DTiles(url, customId);
2043
+ }
2044
+ }
2045
+ }
2046
+ } catch (e) {
2047
+ console.warn("[PathPreview] Failed to sync 3DTiles:", e);
2048
+ }
2049
+ }
2050
+ /**
2051
+ * 手动添加 3D Tiles 到预览窗口
2052
+ * @param url 3D Tiles URL
2053
+ * @param id 唯一标识符
2054
+ */
2055
+ async add3DTiles(url, id) {
2056
+ const v = this.overlayViewer;
2057
+ if (!v) return void 0;
2058
+ if (this.tilesets.has(id)) {
2059
+ return this.tilesets.get(id);
2060
+ }
2061
+ const C = this.CesiumNS;
2062
+ try {
2063
+ const tileset = await C.Cesium3DTileset.fromUrl(url, {
2064
+ maximumScreenSpaceError: 16
2065
+ });
2066
+ if (tileset) {
2067
+ tileset._customId = id;
2068
+ tileset._url = url;
2069
+ v.scene.primitives.add(tileset);
2070
+ this.tilesets.set(id, tileset);
2071
+ console.log("[PathPreview] Added 3DTiles:", id);
2072
+ }
2073
+ return tileset;
2074
+ } catch (e) {
2075
+ console.warn("[PathPreview] Failed to add 3DTiles:", url, e);
2076
+ return void 0;
2077
+ }
2078
+ }
2079
+ /**
2080
+ * 移除 3D Tiles
2081
+ */
2082
+ remove3DTiles(id) {
2083
+ const v = this.overlayViewer;
2084
+ if (!v) return;
2085
+ const tileset = this.tilesets.get(id);
2086
+ if (tileset) {
2087
+ try {
2088
+ v.scene.primitives.remove(tileset);
2089
+ } catch {
2090
+ }
2091
+ this.tilesets.delete(id);
2092
+ }
2093
+ }
2094
+ /**
2095
+ * 同步场景设置(Globe、天空、雾效等)
2096
+ */
2097
+ syncSceneSettings() {
2098
+ const v = this.overlayViewer;
2099
+ if (!v) return;
2100
+ const s = v.scene;
2101
+ const mainScene = this.mainViewer.scene;
2102
+ try {
2103
+ if (s.globe && mainScene.globe) {
2104
+ s.globe.show = mainScene.globe.show;
2105
+ s.globe.enableLighting = mainScene.globe.enableLighting;
2106
+ s.globe.baseColor = mainScene.globe.baseColor;
2107
+ s.globe.showGroundAtmosphere = mainScene.globe.showGroundAtmosphere;
2108
+ s.globe.depthTestAgainstTerrain = mainScene.globe.depthTestAgainstTerrain;
2109
+ }
2110
+ if (mainScene.skyBox) {
2111
+ s.skyBox = mainScene.skyBox;
2112
+ } else {
2113
+ s.skyBox = void 0;
2114
+ }
2115
+ if (mainScene.skyAtmosphere) {
2116
+ s.skyAtmosphere.show = mainScene.skyAtmosphere.show;
2117
+ }
2118
+ if (s.fog && mainScene.fog) {
2119
+ s.fog.enabled = mainScene.fog.enabled;
2120
+ s.fog.density = mainScene.fog.density;
2121
+ }
2122
+ v.shadows = this.mainViewer.shadows;
2123
+ if (s.postProcessStages?.fxaa && mainScene.postProcessStages?.fxaa) {
2124
+ s.postProcessStages.fxaa.enabled = mainScene.postProcessStages.fxaa.enabled;
2125
+ }
2126
+ } catch (e) {
2127
+ console.warn("[PathPreview] Failed to sync scene settings:", e);
2128
+ }
2129
+ }
2130
+ /**
2131
+ * 禁用用户交互,相机仅通过 setPose() 控制
2132
+ */
2133
+ disableUserInteraction() {
2134
+ const v = this.overlayViewer;
2135
+ if (!v) return;
2136
+ const s = v.scene;
2137
+ try {
2138
+ const scc = s.screenSpaceCameraController;
2139
+ if (scc) {
2140
+ scc.enableInputs = false;
2141
+ scc.enableRotate = false;
2142
+ scc.enableTranslate = false;
2143
+ scc.enableZoom = false;
2144
+ scc.enableTilt = false;
2145
+ scc.enableLook = false;
2146
+ scc.inertiaSpin = 0;
2147
+ scc.inertiaZoom = 0;
2148
+ scc.inertiaTranslate = 0;
2149
+ }
2150
+ const canvas = s?.canvas ?? v.scene?.canvas;
2151
+ if (canvas && canvas.style) {
2152
+ canvas.style.pointerEvents = "none";
2153
+ try {
2154
+ canvas.setAttribute("tabindex", "-1");
2155
+ } catch {
2156
+ }
2157
+ }
2158
+ v.trackedEntity = void 0;
2159
+ if (v.clock) {
2160
+ v.clock.shouldAnimate = false;
2161
+ }
2162
+ } catch (e) {
2163
+ console.warn("[PathPreview] Failed to disable user interaction:", e);
2164
+ }
2165
+ }
2166
+ /**
2167
+ * 刷新同步(运行时更新)
2168
+ */
2169
+ refresh() {
2170
+ this.syncFromMainViewer();
1975
2171
  }
1976
2172
  /**
1977
2173
  * 创建 FOV 控制器
@@ -2133,30 +2329,63 @@ var PathPreview = class {
2133
2329
  this.fovController.hide();
2134
2330
  }
2135
2331
  }
2136
- // destroy(): void {
2137
- // if (this.destroyed) return;
2138
- // this.destroyed = true;
2139
- // // try {
2140
- // // if (this.footprintEntity) (this.layer.entities as any).remove(this.footprintEntity);
2141
- // // } catch {}
2142
- // this.footprintEntity = undefined;
2143
- //
2144
- // // 销毁 FOV 控制器
2145
- // try {
2146
- // this.fovController?.destroy();
2147
- // } catch {}
2148
- // this.fovController = undefined;
2149
- //
2150
- // try {
2151
- // (this.overlayViewer as any)?.destroy?.();
2152
- // } catch {}
2153
- // this.overlayViewer = undefined;
2154
- // try {
2155
- // if (this.containerEl && this.containerEl.parentElement)
2156
- // this.containerEl.parentElement.removeChild(this.containerEl);
2157
- // } catch {}
2158
- // this.containerEl = undefined;
2159
- // }
2332
+ /**
2333
+ * 显示预览窗口
2334
+ */
2335
+ show() {
2336
+ if (this.containerEl) {
2337
+ this.containerEl.style.display = "block";
2338
+ }
2339
+ }
2340
+ /**
2341
+ * 隐藏预览窗口
2342
+ */
2343
+ hide() {
2344
+ if (this.containerEl) {
2345
+ this.containerEl.style.display = "none";
2346
+ }
2347
+ }
2348
+ /**
2349
+ * 获取预览 viewer 实例
2350
+ */
2351
+ getOverlayViewer() {
2352
+ return this.overlayViewer;
2353
+ }
2354
+ /**
2355
+ * 销毁预览窗口,释放所有资源
2356
+ */
2357
+ destroy() {
2358
+ if (this.destroyed) return;
2359
+ this.destroyed = true;
2360
+ try {
2361
+ this.fovController?.destroy();
2362
+ } catch {
2363
+ }
2364
+ this.fovController = void 0;
2365
+ this.footprintEntity = void 0;
2366
+ try {
2367
+ this.tilesets.forEach((tileset) => {
2368
+ try {
2369
+ this.overlayViewer?.scene?.primitives?.remove?.(tileset);
2370
+ } catch {
2371
+ }
2372
+ });
2373
+ this.tilesets.clear();
2374
+ } catch {
2375
+ }
2376
+ try {
2377
+ this.overlayViewer?.destroy?.();
2378
+ } catch {
2379
+ }
2380
+ this.overlayViewer = void 0;
2381
+ try {
2382
+ if (this.containerEl && this.containerEl.parentElement) {
2383
+ this.containerEl.parentElement.removeChild(this.containerEl);
2384
+ }
2385
+ } catch {
2386
+ }
2387
+ this.containerEl = void 0;
2388
+ }
2160
2389
  };
2161
2390
 
2162
2391
  // src/core/path-manager/FrustumPyramid.ts
@@ -4044,6 +4273,7 @@ var PathEditingEventHandler = class {
4044
4273
  __publicField(this, "contextMenuManager");
4045
4274
  __publicField(this, "vertexDragHandler");
4046
4275
  __publicField(this, "callbacks");
4276
+ __publicField(this, "keydownListener");
4047
4277
  this.CesiumNS = options.CesiumNS;
4048
4278
  this.viewer = options.viewer;
4049
4279
  this.hiddenClimbIndex = options.hiddenClimbIndex;
@@ -4168,6 +4398,37 @@ var PathEditingEventHandler = class {
4168
4398
  }
4169
4399
  this.handleRightClick(movement);
4170
4400
  }, C.ScreenSpaceEventType.RIGHT_CLICK);
4401
+ this.keydownListener = (e) => {
4402
+ if (e.code !== "Space") return;
4403
+ const target = e.target;
4404
+ if (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable) {
4405
+ return;
4406
+ }
4407
+ if (this.vertexDragHandler.isDragging()) return;
4408
+ e.preventDefault();
4409
+ this.handleSpaceKeyInsert();
4410
+ };
4411
+ document.addEventListener("keydown", this.keydownListener);
4412
+ }
4413
+ /**
4414
+ * 处理空格键快捷插入航点
4415
+ * 在飞机游标当前位置,将航点添加到所有航点末尾
4416
+ */
4417
+ handleSpaceKeyInsert() {
4418
+ const airplaneCursor = this.callbacks.getAirplaneCursor?.();
4419
+ if (!airplaneCursor) {
4420
+ console.warn("[PathEditingEventHandler] \u7A7A\u683C\u952E\u63D2\u5165\uFF1A\u98DE\u673A\u6E38\u6807\u4E0D\u5B58\u5728");
4421
+ return;
4422
+ }
4423
+ const pose = airplaneCursor.getPose();
4424
+ if (!pose || !pose.position) {
4425
+ console.warn("[PathEditingEventHandler] \u7A7A\u683C\u952E\u63D2\u5165\uFF1A\u65E0\u6CD5\u83B7\u53D6\u6E38\u6807\u59FF\u6001");
4426
+ return;
4427
+ }
4428
+ const positions = this.callbacks.getPositions?.() || [];
4429
+ const insertAt = positions.length;
4430
+ console.log("[PathEditingEventHandler] \u7A7A\u683C\u952E\u5FEB\u6377\u63D2\u5165\u822A\u70B9\uFF0C\u4F4D\u7F6E\u7D22\u5F15:", insertAt);
4431
+ this.handleInsertVertex(insertAt, pose, "after");
4171
4432
  }
4172
4433
  /**
4173
4434
  * 处理右键点击事件
@@ -4182,12 +4443,13 @@ var PathEditingEventHandler = class {
4182
4443
  const entity = picked?.id;
4183
4444
  const airplaneCursor = this.callbacks.getAirplaneCursor?.();
4184
4445
  const vertexIndex = pickVertexIndex(this.viewer, movement.position, this.hiddenClimbIndex);
4446
+ const viewportPosition = this.canvasToViewportPosition(movement.position);
4185
4447
  if (typeof vertexIndex === "number") {
4186
4448
  const menuItems = this.buildVertexContextMenuItems(vertexIndex);
4187
- this.contextMenuManager.show(movement.position, menuItems);
4449
+ this.contextMenuManager.show(viewportPosition, menuItems);
4188
4450
  } else if (airplaneCursor && airplaneCursor.containsEntity(entity)) {
4189
4451
  const menuItems = this.buildAirplaneCursorContextMenuItems();
4190
- this.contextMenuManager.show(movement.position, menuItems);
4452
+ this.contextMenuManager.show(viewportPosition, menuItems);
4191
4453
  }
4192
4454
  } catch (error) {
4193
4455
  console.error("Error handling right click:", error);
@@ -4491,6 +4753,18 @@ var PathEditingEventHandler = class {
4491
4753
  return 0;
4492
4754
  }
4493
4755
  }
4756
+ /**
4757
+ * 将 canvas 坐标转换为视口坐标
4758
+ * Cesium 事件返回的坐标是相对于 canvas 的,而菜单需要相对于视口的坐标
4759
+ */
4760
+ canvasToViewportPosition(canvasPosition) {
4761
+ const canvas = this.viewer.scene.canvas;
4762
+ const rect = canvas.getBoundingClientRect();
4763
+ return {
4764
+ x: canvasPosition.x + rect.left,
4765
+ y: canvasPosition.y + rect.top
4766
+ };
4767
+ }
4494
4768
  /**
4495
4769
  * 🆕 从鼠标事件获取位置
4496
4770
  */
@@ -4516,6 +4790,10 @@ var PathEditingEventHandler = class {
4516
4790
  * 销毁事件处理器
4517
4791
  */
4518
4792
  destroy() {
4793
+ if (this.keydownListener) {
4794
+ document.removeEventListener("keydown", this.keydownListener);
4795
+ this.keydownListener = void 0;
4796
+ }
4519
4797
  try {
4520
4798
  this.vertexDragHandler.destroy();
4521
4799
  } catch {