@yangyongtao/gaea 1.1.34 → 1.1.35

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.
@@ -1,10 +1,13 @@
1
- /**
1
+ /**
2
2
  * gaea-3dview npm package
3
3
  *
4
- * 鍒濆鍖栧湪 new GaeaApp() 鏃惰嚜鍔ㄥ畬鎴愶紝Vue 椤甸潰鍙渶閰嶇疆銆? *
5
- * 鐢ㄦ硶锛? * setConfig({ serveUrl: '...', serveLocal: '...' });
4
+ * 初始化在 new GaeaApp() 时自动完成,Vue 页面只需配置。
5
+ *
6
+ * 用法:
7
+ * setConfig({ serveUrl: '...', serveLocal: '...' });
6
8
  * const app = new GaeaApp();
7
- * await app.ready; // 鍙€夛紝绛夊緟鍒濆鍖栧畬鎴? */
9
+ * await app.ready; // 可选,等待初始化完成
10
+ */
8
11
 
9
12
  import { Group, Tween, Easing } from '@tweenjs/tween.js';
10
13
  window.TWEEN = { Group, Tween, Easing };
@@ -12,7 +15,7 @@ window.TWEEN = { Group, Tween, Easing };
12
15
  import Gaea from '../public/index.js';
13
16
 
14
17
 
15
- // ==================== Loading 鍔ㄧ敾 ====================
18
+ // ==================== Loading 动画 ====================
16
19
 
17
20
  (function injectLoadingStyle() {
18
21
  if (document.getElementById('gaea-loading-style')) return;
@@ -51,7 +54,7 @@ import Gaea from '../public/index.js';
51
54
  transition: background 0.2s; backdrop-filter: blur(4px);
52
55
  }
53
56
  #gaea-layertree-btn:hover { background: rgba(0,0,0,0.7); }
54
- /* 灞忓箷鍥炬爣鏍峰紡锛堝榻?ht_3dview锛?*/
57
+ /* 屏幕图标样式(对齐 ht_3dview) */
55
58
  .gaea-locationLineStyle {
56
59
  position: absolute; overflow: hidden; z-index: 1; pointer-events: auto;
57
60
  user-select: none; -webkit-user-select: none;
@@ -114,28 +117,30 @@ function hideLoading() {
114
117
  }
115
118
 
116
119
 
117
- // ==================== 閰嶇疆 ====================
120
+ // ==================== 配置 ====================
118
121
 
119
- /** 榛樿寮曟搸璧勬簮鏈嶅姟鍣ㄥ湴鍧€锛圙aea.pck / Gaea.wasm 绛夊ぇ鏂囦欢锛夛紝鍙湪杩愯鏃?鐜鍙橀噺瑕嗙洊 */
122
+ /** 默认引擎资源服务器地址(Gaea.pck / Gaea.wasm 等大文件),可在运行时/环境变量覆盖 */
120
123
  const DEFAULT_ENGINE_ASSETS_URL = 'http://172.20.176.100:7072/gaea/Gaea';
121
124
 
122
125
  /**
123
- * 瑙f瀽榛樿 executable锛? * 浼樺厛绾э細杩愯鏃跺叏灞€鍙橀噺 window.__GAEA_ASSETS_URL__锛堝涓荤珯鐐?config.js 鍙缃紝
124
- * 鎹㈠湴鍧€鏃犻渶閲嶆柊鎵撳寘锛? dev 鐜鏈湴 /Gaea锛堣蛋 vite dev server 涓棿浠讹紝涓嶄緷璧栫嚎涓婏級
125
- * > 鏋勫缓鐜鍙橀噺 VITE_GAEA_EXECUTABLE > 鍐呯疆榛樿璧勬簮鏈嶅姟鍣ㄥ湴鍧€銆? */
126
+ * 解析默认 executable:
127
+ * 优先级:运行时全局变量 window.__GAEA_ASSETS_URL__(宿主站点 config.js 可设置,
128
+ * 换地址无需重新打包)> dev 环境本地 /Gaea(走 vite dev server 中间件,不依赖线上)
129
+ * > 构建环境变量 VITE_GAEA_EXECUTABLE > 内置默认资源服务器地址。
130
+ */
126
131
  function resolveDefaultExecutable() {
127
132
  if (typeof window !== 'undefined' && window.__GAEA_ASSETS_URL__) {
128
133
  return window.__GAEA_ASSETS_URL__;
129
134
  }
130
135
  try {
131
136
  if (import.meta.env) {
132
- // dev 寮€鍙戯細寮曟搸璧勬簮璧?vite dev server 涓棿浠讹紙鏈湴 gaea public锛夛紝鏃犻渶璁块棶绾夸笂
137
+ // dev 开发:引擎资源走 vite dev server 中间件(本地 gaea public),无需访问线上
133
138
  if (import.meta.env.DEV) return '/Gaea';
134
- // 鏋勫缓鏃跺彲鐢ㄧ幆澧冨彉閲忚鐩栵紙濡傛湰鍦版祴璇?build 浜х墿鎸囧悜鏈湴璧勬簮鏈嶅姟鍣級
139
+ // 构建时可用环境变量覆盖(如本地测试 build 产物指向本地资源服务器)
135
140
  if (import.meta.env.VITE_GAEA_EXECUTABLE) return import.meta.env.VITE_GAEA_EXECUTABLE;
136
141
  }
137
142
  } catch (e) {
138
- /* 闈?vite 鐜蹇界暐 */
143
+ /* 非 vite 环境忽略 */
139
144
  }
140
145
  return DEFAULT_ENGINE_ASSETS_URL;
141
146
  }
@@ -143,151 +148,153 @@ function resolveDefaultExecutable() {
143
148
  const defaultConfig = {
144
149
  serveUrl: '',
145
150
  serveLocal: '',
146
- // Engine 閰嶇疆
151
+ // Engine 配置
147
152
  executable: resolveDefaultExecutable(),
148
153
  fileSizes: { 'Gaea.pck': 450751936, 'Gaea.wasm': 16580215 },
149
154
  canvasResizePolicy: 2,
150
155
  experimentalVK: false,
151
156
  focusCanvas: true,
152
157
  /**
153
- * 鏈嶅姟妯″紡锛? * 'platform' - 鏈嶅姟骞冲彴锛氬奖鍍忚蛋 serveUrl 鐨?WMTS锛屽苟鍔犺浇鍦板舰銆佽创鍦扮嚎
154
- * 'online' - 鍦ㄧ嚎锛氫粎鍔犺浇澶╁湴鍥惧奖鍍忥紝涓嶅姞杞藉湴褰㈠拰璐村湴绾? */
158
+ * 服务模式:
159
+ * 'platform' - 服务平台:影像走 serveUrl 的 WMTS,并加载地形、贴地线
160
+ * 'online' - 在线:仅加载天地图影像,不加载地形和贴地线
161
+ */
155
162
  serviceMode: 'platform',
156
- /** 澶╁湴鍥?token锛堝湪绾挎ā寮忎娇鐢級 */
163
+ /** 天地图 token(在线模式使用) */
157
164
  tiandituToken: '63201cfd7df1e1bff7d88bb8baa32151',
158
- /** 澶╁湴鍥炬渶灏?鏈€澶у眰绾?*/
165
+ /** 天地图最小/最大层级 */
159
166
  tiandituLevelRange: [0, 21],
160
- /** 榛樿鍔犺浇鐨勫奖鍍忓浘灞傦紙鏈嶅姟骞冲彴妯″紡鐢熸晥锛?*/
167
+ /** 默认加载的影像图层(服务平台模式生效) */
161
168
  defaultLayers: [
162
169
  { layerName: 'earthland', forMat: 'image/png', min: 0, max: 18, transparency: 0.3 },
163
- { layerName: '鑻嶅崡L19', forMat: 'image/png', min: 5, max: 17 },
170
+ { layerName: '苍南L19', forMat: 'image/png', min: 5, max: 17 },
164
171
  ],
165
- /** 榛樿鍔犺浇鐨勫湴褰㈡湇鍔★紙璧?serveUrl锛寀rl 涓虹浉瀵硅矾寰勬垨瀹屾暣鍦板潃锛涚┖鏁扮粍鍒欎笉鍔犺浇锛?*/
172
+ /** 默认加载的地形服务(走 serveUrl,url 为相对路径或完整地址;空数组则不加载) */
166
173
  defaultTerrains: [
167
- { name: '鑻嶅崡鍘垮湴褰?, url: 'htc/service/tms/1.0.0/鑻嶅崡鍘垮湴褰EPSG%3A4326@terrain/' },
174
+ { name: '苍南县地形', url: 'htc/service/tms/1.0.0/苍南县地形@EPSG%3A4326@terrain/' },
168
175
  ],
169
- /** 榛樿鍔犺浇鐨勮创鍦扮嚎锛堢煝閲忕嚎鏈嶅姟锛屼緷璧?WMTS 鏈嶅姟鍒楄〃锛涚┖鏁扮粍鍒欎笉鍔犺浇锛?*/
176
+ /** 默认加载的贴地线(矢量线服务,依赖 WMTS 服务列表;空数组则不加载) */
170
177
  defaultGroundLines: [
171
- { name: '鑻嶅崡鍘胯竟鐣?, range: [0, 9], color: [255, 255, 0], index: 100 },
172
- { name: '鑻嶅崡鍘块晣杈圭晫', range: [9, 15], color: [0, 255, 0], index: 80 },
173
- { name: '鑻嶅崡鍘挎潙杈圭晫', range: [15, 19], color: [255, 255, 255], index: 60 },
174
- { name: '娌挎郸娴峰宸ョ▼鑼冨洿绾?, range: [0, 21], color: [0, 255, 255], index: 120 },
178
+ { name: '苍南县边界', range: [0, 9], color: [255, 255, 0], index: 100 },
179
+ { name: '苍南县镇边界', range: [9, 15], color: [0, 255, 0], index: 80 },
180
+ { name: '苍南县村边界', range: [15, 19], color: [255, 255, 255], index: 60 },
181
+ { name: '沿浦海塘工程范围线', range: [0, 21], color: [0, 255, 255], index: 120 },
175
182
  ],
176
- /** 榛樿鍔犺浇鐨勫€炬枩鎽勫奖 3D Tiles锛堢┖鏁扮粍鍒欎笉鍔犺浇锛?*/
183
+ /** 默认加载的倾斜摄影 3D Tiles(空数组则不加载) */
177
184
  default3DTiles: [],
178
- /** 榛樿鐩告満瑙嗚 */
185
+ /** 默认相机视角 */
179
186
  defaultCameraPosition: '27.3680928977018,120.414139621887,0,109379.690132486,0,0',
180
- /** 寮曟搸鍔犺浇杩涘害鍥炶皟 */
187
+ /** 引擎加载进度回调 */
181
188
  onProgress: null,
182
- /** 寮曟搸鍔犺浇鎻愮ず鏂囧瓧 */
183
- loadingText: 'GAEA 寮曟搸鍔犺浇涓?..',
184
- /** DataHub API 鍦板潃锛堝浘灞傛爲鏁版嵁鎺ュ彛锛夛紝涓虹┖鍒欏彧鐢ㄩ粯璁ゆ暟鎹?*/
189
+ /** 引擎加载提示文字 */
190
+ loadingText: 'GAEA 引擎加载中...',
191
+ /** DataHub API 地址(图层树数据接口),为空则只用默认数据 */
185
192
  dataHubApiUrl: '',
186
- /** 鏄惁鏄剧ず鍥惧眰鏍戞寜閽紙鍙充笂瑙掗噸缃寜閽梺杈癸級 */
193
+ /** 是否显示图层树按钮(右上角重置按钮旁边) */
187
194
  showLayerTreeBtn: false,
188
- /** 榛樿闅愯棌鐨勫垎绫?key 鍒楄〃锛屽 ['rainStations', 'rescue'] */
195
+ /** 默认隐藏的分类 key 列表,如 ['rainStations', 'rescue'] */
189
196
  defaultHiddenCategories: [],
190
- /** 鍥惧眰鏍戝垎绫绘暟鎹紙榛樿鏁版嵁锛屾帴鍙f暟鎹細鍚堝苟杩涙潵锛?*/
197
+ /** 图层树分类数据(默认数据,接口数据会合并进来) */
191
198
  layerCategories: [
192
- { key: 'seawall', text: '娴峰', visible: true, children: [
193
- { text: '娌挎郸娴峰', level: '3', imgUrl: '/image/screenIcon/娴峰-3绾?png', position: { x: 27.220705, y: 120.457998, z: 20 } },
194
- { text: '闇炲叧娴峰', level: '3', imgUrl: '/image/screenIcon/娴峰-3绾?png', position: { x: 27.171059, y: 120.467901, z: 20 } },
195
- { text: '涓夎寙娴峰', level: '4', imgUrl: '/image/screenIcon/娴峰-4绾?png', position: { x: 27.193284, y: 120.464411, z: 20 } },
196
- { text: '澶ф笖娴峰', level: '2', imgUrl: '/image/screenIcon/娴峰-2绾?png', position: { x: 27.39027, y: 120.599554, z: 20 } },
197
- { text: '淇℃櫤娴峰', level: '4', imgUrl: '/image/screenIcon/娴峰-4绾?png', position: { x: 27.300806, y: 120.544077, z: 20 } },
198
- { text: '涓嬪湪娴峰', level: '3', imgUrl: '/image/screenIcon/娴峰-3绾?png', position: { x: 27.215789, y: 120.445849, z: 10 } },
199
- { text: '鏈ㄦ灄娴峰', level: '5', imgUrl: '/image/screenIcon/娴峰-5绾?png', position: { x: 27.193183, y: 120.424486, z: 20 } },
200
- { text: '闆惧煄娴峰', level: '4', imgUrl: '/image/screenIcon/娴峰-4绾?png', position: { x: 27.27682, y: 120.518917, z: 20 } },
201
- { text: '娓斿矙娴峰', level: '4', imgUrl: '/image/screenIcon/娴峰-4绾?png', position: { x: 27.403744, y: 120.557879, z: 20 } },
202
- { text: '娴佸矏宀欐捣濉?, level: '4', imgUrl: '/image/screenIcon/娴峰-4绾?png', position: { x: 27.318176, y: 120.542761, z: 20 } },
199
+ { key: 'seawall', text: '海塘', visible: true, children: [
200
+ { text: '沿浦海塘', level: '3', imgUrl: '/image/screenIcon/海塘-3级.png', position: { x: 27.220705, y: 120.457998, z: 20 } },
201
+ { text: '霞关海塘', level: '3', imgUrl: '/image/screenIcon/海塘-3级.png', position: { x: 27.171059, y: 120.467901, z: 20 } },
202
+ { text: '三茆海塘', level: '4', imgUrl: '/image/screenIcon/海塘-4级.png', position: { x: 27.193284, y: 120.464411, z: 20 } },
203
+ { text: '大渔海塘', level: '2', imgUrl: '/image/screenIcon/海塘-2级.png', position: { x: 27.39027, y: 120.599554, z: 20 } },
204
+ { text: '信智海塘', level: '4', imgUrl: '/image/screenIcon/海塘-4级.png', position: { x: 27.300806, y: 120.544077, z: 20 } },
205
+ { text: '下在海塘', level: '3', imgUrl: '/image/screenIcon/海塘-3级.png', position: { x: 27.215789, y: 120.445849, z: 10 } },
206
+ { text: '木林海塘', level: '5', imgUrl: '/image/screenIcon/海塘-5级.png', position: { x: 27.193183, y: 120.424486, z: 20 } },
207
+ { text: '雾城海塘', level: '4', imgUrl: '/image/screenIcon/海塘-4级.png', position: { x: 27.27682, y: 120.518917, z: 20 } },
208
+ { text: '渔岙海塘', level: '4', imgUrl: '/image/screenIcon/海塘-4级.png', position: { x: 27.403744, y: 120.557879, z: 20 } },
209
+ { text: '流岐岙海塘', level: '4', imgUrl: '/image/screenIcon/海塘-4级.png', position: { x: 27.318176, y: 120.542761, z: 20 } },
203
210
  ]},
204
- { key: 'hydrometricStations', text: '姘存枃绔?, visible: true, children: [
205
- { text: '鐏垫邯', stcd: '7010CK01', sttp: 'ZQ', position: { x: 27.5014, y: 120.3847, z: 100 } },
206
- { text: '娴佺煶', stcd: '7010CK02', sttp: 'ZQ', position: { x: 27.5029, y: 120.4615, z: 100 } },
207
- { text: '瑙傜編', stcd: '7050CK02', sttp: 'ZQ', position: { x: 27.4658, y: 120.364, z: 100 } },
208
- { text: '妗傚叞婧?, stcd: '7061CK68', sttp: 'ZQ', position: { x: 27.4584, y: 120.3071, z: 100 } },
211
+ { key: 'hydrometricStations', text: '水文站', visible: true, children: [
212
+ { text: '灵溪', stcd: '7010CK01', sttp: 'ZQ', position: { x: 27.5014, y: 120.3847, z: 100 } },
213
+ { text: '流石', stcd: '7010CK02', sttp: 'ZQ', position: { x: 27.5029, y: 120.4615, z: 100 } },
214
+ { text: '观美', stcd: '7050CK02', sttp: 'ZQ', position: { x: 27.4658, y: 120.364, z: 100 } },
215
+ { text: '桂兰溪', stcd: '7061CK68', sttp: 'ZQ', position: { x: 27.4584, y: 120.3071, z: 100 } },
209
216
  ]},
210
- { key: 'tideStations', text: '娼綅绔?, visible: true, children: [] },
211
- { key: 'riverStations', text: '娌抽亾姘翠綅绔?, visible: true, children: [] },
212
- { key: 'rainStations', text: '闆ㄩ噺绔?, visible: true, children: [] },
213
- { key: 'reservoirs', text: '姘村簱', visible: true, children: [] },
214
- { key: 'sluice', text: '姘撮椄', visible: true, children: [
215
- { text: '鑱旂洘姘撮椄', position: { x: 27.200462, y: 120.465635, z: 50 } },
216
- { text: '娌挎郸姘撮椄', position: { x: 27.222987, y: 120.453175, z: 20 } },
217
- { text: '涓嬪湪姘撮椄', position: { x: 27.221816, y: 120.445182, z: 50 } },
218
- { text: '涓嬪湪娴峰绾虫帓闂?, position: { x: 27.219978, y: 120.445672, z: 50 } },
219
- { text: '宀熬姘撮椄', position: { x: 27.215762, y: 120.462235, z: 40 } },
220
- { text: '宀熬鏂伴椄', position: { x: 27.210369, y: 120.458532, z: 10 } },
217
+ { key: 'tideStations', text: '潮位站', visible: true, children: [] },
218
+ { key: 'riverStations', text: '河道水位站', visible: true, children: [] },
219
+ { key: 'rainStations', text: '雨量站', visible: true, children: [] },
220
+ { key: 'reservoirs', text: '水库', visible: true, children: [] },
221
+ { key: 'sluice', text: '水闸', visible: true, children: [
222
+ { text: '联盟水闸', position: { x: 27.200462, y: 120.465635, z: 50 } },
223
+ { text: '沿浦水闸', position: { x: 27.222987, y: 120.453175, z: 20 } },
224
+ { text: '下在水闸', position: { x: 27.221816, y: 120.445182, z: 50 } },
225
+ { text: '下在海塘纳排闸', position: { x: 27.219978, y: 120.445672, z: 50 } },
226
+ { text: '岭尾水闸', position: { x: 27.215762, y: 120.462235, z: 40 } },
227
+ { text: '岭尾新闸', position: { x: 27.210369, y: 120.458532, z: 10 } },
221
228
  ]},
222
- { key: 'secData', text: '鏂潰鏁版嵁', visible: true, children: [
229
+ { key: 'secData', text: '断面数据', visible: true, children: [
223
230
  { text: 'YPDP0+600', position: { x: 27.217692, y: 120.461823, z: 30 },
224
231
  indexList: {
225
- 1: { label: '绱犲~鍦?, details: ['鎴愬垎: 浜哄伐鍥炲~榛忔€у湡', '鍘氬害: 绾?.5m', '浣滅敤: 琛ㄥ眰瑕嗙洊闃叉姢'] },
226
- 2: { label: '娣ゆ偿', details: ['绫诲埆: 娴风浉娣ゆ偿', '棰滆壊: 鐏伴粦鑹?, '鐘舵€? 娴佸', '鍚按鐜? 绾?5%'] },
227
- 3: { label: '6.89', details: ['绫诲瀷: 鍫ら《璁捐楂樼▼', '鏁板€? 6.89m', '浣滅敤: 闃叉椽鍫ゅ潩椤堕儴鏍囬珮'] },
228
- 4: { label: '6.00', details: ['绫诲瀷: 璁捐娲按浣?, '鏁板€? 6.00m', '鏍囧噯: 閲嶇幇鏈烶=5%'] },
229
- 5: { label: '妞嶈鎶ゅ潯', details: ['绫诲瀷: 鑽夌毊鎶ゅ潯', '鍧″害: 1:2', '浣滅敤: 闃叉鍧¢潰闆ㄦ按鍐插埛'] },
230
- 6: { label: '3.00', details: ['绫诲瀷: 甯告按浣?, '鏁板€? 3.00m', '浣滅敤: 鏃ュ父杩愯姘翠綅绾?] },
231
- 7: { label: '鐮栫煶娣峰悎鏂欏~绛?, details: ['鎴愬垎: 鐮栫煶涓庨粡鍦熸贩鍚?, '鍘氬害: 绾?.5m', '浣滅敤: 鍫よ韩濉瓚涓讳綋'] },
232
- 8: { label: '2.00', details: ['绫诲瀷: 鍫よ剼楂樼▼', '鏁板€? 2.00m', '浣滅敤: 鍫ゅ潩搴曢儴鍩虹鏍囬珮'] },
233
- 9: { label: '3.94璁捐娲按P=5%', details: ['绫诲瀷: 璁捐娲按浣?, '棰戠巼: 5%(20骞翠竴閬?', '鏁板€? 3.94m', '浣滅敤: 鍫ら槻璁捐鏍囧噯姘翠綅'] },
234
- 10: { label: '4.5', details: ['绫诲瀷: 璁捐楂樻疆浣?, '鏍囧噯: 50骞翠竴閬?, '鏁板€? 4.45m', '浣滅敤: 娴峰牑闃叉疆鏍囧噯'] },
235
- 11: { label: '50骞翠竴閬囪璁¢珮娼綅4.45m', details: ['绫诲瀷: 璁捐楂樻疆浣?, '閲嶇幇鏈? 50骞?, '鏁板€? 4.45m', '浣滅敤: 鏍℃牳鍫ら《楂樼▼'] },
232
+ 1: { label: '素填土', details: ['成分: 人工回填黏性土', '厚度: 约1.5m', '作用: 表层覆盖防护'] },
233
+ 2: { label: '淤泥', details: ['类别: 海相淤泥', '颜色: 灰黑色', '状态: 流塑', '含水率: 约55%'] },
234
+ 3: { label: '6.89', details: ['类型: 堤顶设计高程', '数值: 6.89m', '作用: 防洪堤坝顶部标高'] },
235
+ 4: { label: '6.00', details: ['类型: 设计洪水位', '数值: 6.00m', '标准: 重现期P=5%'] },
236
+ 5: { label: '植被护坡', details: ['类型: 草皮护坡', '坡度: 1:2', '作用: 防止坡面雨水冲刷'] },
237
+ 6: { label: '3.00', details: ['类型: 常水位', '数值: 3.00m', '作用: 日常运行水位线'] },
238
+ 7: { label: '砖石混合料填筑', details: ['成分: 砖石与黏土混合', '厚度: 约2.5m', '作用: 堤身填筑主体'] },
239
+ 8: { label: '2.00', details: ['类型: 堤脚高程', '数值: 2.00m', '作用: 堤坝底部基础标高'] },
240
+ 9: { label: '3.94设计洪水P=5%', details: ['类型: 设计洪水位', '频率: 5%(20年一遇)', '数值: 3.94m', '作用: 堤防设计标准水位'] },
241
+ 10: { label: '4.5', details: ['类型: 设计高潮位', '标准: 50年一遇', '数值: 4.45m', '作用: 海堤防潮标准'] },
242
+ 11: { label: '50年一遇设计高潮位4.45m', details: ['类型: 设计高潮位', '重现期: 50年', '数值: 4.45m', '作用: 校核堤顶高程'] },
236
243
  },
237
244
  favorite: '27.2201168867619,120.460489990829,152.923049719807,297.140390154652,50.2192727515776,0.00126576050689737',
238
245
  secAnimation: [{x:27.217703, y:120.46207, z:32}, {x:27.217626, y:120.461815, z:32}, {x:27.217433, y:120.461448, z:32}] },
239
246
  { text: 'YPDP0+100', position: { x: 27.223027, y: 120.454653, z: 30 },
240
247
  indexList: {
241
- 1: { label: '娣ゆ偿', details: ['绫诲埆: 娴风浉娣ゆ偿', '棰滆壊: 鐏伴粦鑹?, '鐘舵€? 娴佸'] },
242
- 2: { label: '绱犲湡', details: ['绫诲埆: 澶╃劧娌夌Н鍦?, '棰滆壊: 榛勮鑹?, '鐘舵€? 鍙', '鎵胯浇鍔? 涓€鑸?] },
243
- 3: { label: '鐮栫煶娣峰悎鏉愭枡濉瓚', details: ['鎴愬垎: 鐮栫煶涓庨粡鍦熸贩鍚?, '鍘氬害: 绾?m', '浣滅敤: 鍫よ韩濉瓚涓讳綋'] },
244
- 4: { label: '6.98', details: ['绫诲瀷: 鍫ら《璁捐楂樼▼', '鏁板€? 6.98m'] },
245
- 5: { label: '6.00', details: ['绫诲瀷: 璁捐娲按浣?, '鏁板€? 6.00m', '鏍囧噯: P=5%'] },
246
- 6: { label: '3.94', details: ['绫诲瀷: 璁捐娲按浣?, '棰戠巼: 5%', '鏁板€? 3.94m'] },
247
- 7: { label: '2.00', details: ['绫诲瀷: 鍫よ剼楂樼▼', '鏁板€? 2.00m'] },
248
- 8: { label: '4.5', details: ['绫诲瀷: 璁捐楂樻疆浣?, '鏍囧噯: 50骞翠竴閬?, '鏁板€? 4.45m'] },
248
+ 1: { label: '淤泥', details: ['类别: 海相淤泥', '颜色: 灰黑色', '状态: 流塑'] },
249
+ 2: { label: '素土', details: ['类别: 天然沉积土', '颜色: 黄褐色', '状态: 可塑', '承载力: 一般'] },
250
+ 3: { label: '砖石混合材料填筑', details: ['成分: 砖石与黏土混合', '厚度: 约2m', '作用: 堤身填筑主体'] },
251
+ 4: { label: '6.98', details: ['类型: 堤顶设计高程', '数值: 6.98m'] },
252
+ 5: { label: '6.00', details: ['类型: 设计洪水位', '数值: 6.00m', '标准: P=5%'] },
253
+ 6: { label: '3.94', details: ['类型: 设计洪水位', '频率: 5%', '数值: 3.94m'] },
254
+ 7: { label: '2.00', details: ['类型: 堤脚高程', '数值: 2.00m'] },
255
+ 8: { label: '4.5', details: ['类型: 设计高潮位', '标准: 50年一遇', '数值: 4.45m'] },
249
256
  },
250
257
  favorite: '27.2241401069656,120.45184648674,114.05854266096,366.355033916145,42.1285030727423,359.999914392757',
251
258
  secAnimation: [{x:27.223145, y:120.454946, z:30}, {x:27.223085, y:120.454756, z:30}, {x:27.222844, y:120.454466, z:30}] },
252
259
  { text: 'YPDP0+200', position: { x: 27.222541, y: 120.455453, z: 30 },
253
260
  indexList: {
254
- 0: { label: '娣ゆ偿', details: ['绫诲埆: 娴风浉娣ゆ偿', '棰滆壊: 鐏伴粦鑹?, '鐘舵€? 娴佸'] },
255
- 1: { label: '绱犲湡', details: ['绫诲埆: 澶╃劧娌夌Н鍦?, '棰滆壊: 榛勮鑹?, '鐘舵€? 鍙'] },
256
- 2: { label: '鐮栫煶娣峰悎鏉愭枡濉瓚', details: ['鎴愬垎: 鐮栫煶涓庨粡鍦熸贩鍚?, '浣滅敤: 鍫よ韩濉瓚涓讳綋'] },
257
- 3: { label: '6.98', details: ['绫诲瀷: 鍫ら《璁捐楂樼▼', '鏁板€? 6.98m'] },
258
- 4: { label: '6.00', details: ['绫诲瀷: 璁捐娲按浣?, '鏁板€? 6.00m', '鏍囧噯: P=5%'] },
259
- 5: { label: '3.94', details: ['绫诲瀷: 璁捐娲按浣?, '棰戠巼: 5%', '鏁板€? 3.94m'] },
260
- 6: { label: '2.00', details: ['绫诲瀷: 鍫よ剼楂樼▼', '鏁板€? 2.00m'] },
261
- 7: { label: '4.5', details: ['绫诲瀷: 璁捐楂樻疆浣?, '鏍囧噯: 50骞翠竴閬?, '鏁板€? 4.45m'] },
261
+ 0: { label: '淤泥', details: ['类别: 海相淤泥', '颜色: 灰黑色', '状态: 流塑'] },
262
+ 1: { label: '素土', details: ['类别: 天然沉积土', '颜色: 黄褐色', '状态: 可塑'] },
263
+ 2: { label: '砖石混合材料填筑', details: ['成分: 砖石与黏土混合', '作用: 堤身填筑主体'] },
264
+ 3: { label: '6.98', details: ['类型: 堤顶设计高程', '数值: 6.98m'] },
265
+ 4: { label: '6.00', details: ['类型: 设计洪水位', '数值: 6.00m', '标准: P=5%'] },
266
+ 5: { label: '3.94', details: ['类型: 设计洪水位', '频率: 5%', '数值: 3.94m'] },
267
+ 6: { label: '2.00', details: ['类型: 堤脚高程', '数值: 2.00m'] },
268
+ 7: { label: '4.5', details: ['类型: 设计高潮位', '标准: 50年一遇', '数值: 4.45m'] },
262
269
  },
263
270
  favorite: '27.2239199251089,120.452312356798,114.058543530715,366.288499980401,42.128503423954,359.999913220015',
264
271
  secAnimation: [{x:27.222801, y:120.455528, z:30}, {x:27.222681, y:120.455458, z:30}, {x:27.22226, y:120.455318, z:30}] },
265
272
  { text: 'YPDP0+750', position: { x: 27.220289, y: 120.459753, z: 30 },
266
273
  indexList: {
267
- 0: { label: '娣ゆ偿', details: ['绫诲埆: 娴风浉娣ゆ偿', '棰滆壊: 鐏伴粦鑹?, '鐘舵€? 娴佸'] },
268
- 1: { label: '鐮栫煶娣峰悎鏂欏~绛?, details: ['鎴愬垎: 鐮栫煶涓庨粡鍦熸贩鍚?, '鍘氬害: 绾?.5m', '浣滅敤: 鍫よ韩濉瓚涓讳綋'] },
269
- 2: { label: '7.00', details: ['绫诲瀷: 鍫ら《璁捐楂樼▼', '鏁板€? 7.00m'] },
270
- 3: { label: '6.00', details: ['绫诲瀷: 璁捐娲按浣?, '鏁板€? 6.00m', '鏍囧噯: P=5%'] },
271
- 4: { label: '3.94', details: ['绫诲瀷: 璁捐娲按浣?, '棰戠巼: 5%', '鏁板€? 3.94m'] },
272
- 5: { label: '2.00', details: ['绫诲瀷: 鍫よ剼楂樼▼', '鏁板€? 2.00m'] },
273
- 6: { label: '4.5', details: ['绫诲瀷: 璁捐楂樻疆浣?, '鏍囧噯: 50骞翠竴閬?, '鏁板€? 4.45m'] },
274
- 7: { label: '妞嶈鎶ゅ潯', details: ['绫诲瀷: 鑽夌毊鎶ゅ潯', '鍧″害: 1:2', '浣滅敤: 闃叉鍧¢潰鍐插埛'] },
274
+ 0: { label: '淤泥', details: ['类别: 海相淤泥', '颜色: 灰黑色', '状态: 流塑'] },
275
+ 1: { label: '砖石混合料填筑', details: ['成分: 砖石与黏土混合', '厚度: 约2.5m', '作用: 堤身填筑主体'] },
276
+ 2: { label: '7.00', details: ['类型: 堤顶设计高程', '数值: 7.00m'] },
277
+ 3: { label: '6.00', details: ['类型: 设计洪水位', '数值: 6.00m', '标准: P=5%'] },
278
+ 4: { label: '3.94', details: ['类型: 设计洪水位', '频率: 5%', '数值: 3.94m'] },
279
+ 5: { label: '2.00', details: ['类型: 堤脚高程', '数值: 2.00m'] },
280
+ 6: { label: '4.5', details: ['类型: 设计高潮位', '标准: 50年一遇', '数值: 4.45m'] },
281
+ 7: { label: '植被护坡', details: ['类型: 草皮护坡', '坡度: 1:2', '作用: 防止坡面冲刷'] },
275
282
  },
276
283
  favorite: '27.2220502555847,120.457317110875,125.021048589548,347.301009802588,44.1512225983844,0.0020422112185327',
277
284
  secAnimation: [{x:27.220482, y:120.459976, z:30}, {x:27.220375, y:120.459853, z:30}, {x:27.220072, y:120.459602, z:30}] },
278
285
  ]},
279
- { key: 'uavNests', text: '鏃犱汉鏈哄发绔?, visible: true, children: [] },
280
- { key: 'rescue', text: '闅愭偅', visible: false, children: [
281
- { text: '闅愭偅1', position: { x: 27.381728, y: 120.587895, z: 20 } },
282
- { text: '闅愭偅2', position: { x: 27.183096, y: 120.48657, z: 20 } },
283
- { text: '闅愭偅3', position: { x: 27.313019, y: 120.538779, z: 20 } },
284
- { text: '闅愭偅4', position: { x: 27.219926, y: 120.455543, z: 20 } },
285
- { text: '闅愭偅5', position: { x: 27.266007, y: 120.52789, z: 20 } },
286
+ { key: 'uavNests', text: '无人机巢站', visible: true, children: [] },
287
+ { key: 'rescue', text: '隐患', visible: false, children: [
288
+ { text: '隐患1', position: { x: 27.381728, y: 120.587895, z: 20 } },
289
+ { text: '隐患2', position: { x: 27.183096, y: 120.48657, z: 20 } },
290
+ { text: '隐患3', position: { x: 27.313019, y: 120.538779, z: 20 } },
291
+ { text: '隐患4', position: { x: 27.219926, y: 120.455543, z: 20 } },
292
+ { text: '隐患5', position: { x: 27.266007, y: 120.52789, z: 20 } },
286
293
  ]},
287
- { key: 'materials', text: '鐗╄祫浠撳簱', visible: false, children: [
288
- { text: '浠撳簱1', position: { x: 27.220674, y: 120.460029, z: 1.179 } },
289
- { text: '浠撳簱2', position: { x: 27.227569, y: 120.458844, z: 4.265 } },
290
- { text: '浠撳簱3', position: { x: 27.23543, y: 120.452171, z: 3.29 } },
294
+ { key: 'materials', text: '物资仓库', visible: false, children: [
295
+ { text: '仓库1', position: { x: 27.220674, y: 120.460029, z: 1.179 } },
296
+ { text: '仓库2', position: { x: 27.227569, y: 120.458844, z: 4.265 } },
297
+ { text: '仓库3', position: { x: 27.23543, y: 120.452171, z: 3.29 } },
291
298
  ]},
292
299
  ],
293
300
  };
@@ -300,7 +307,7 @@ export function getConfig() { return _config; }
300
307
 
301
308
  // ==================== GaeaApp ====================
302
309
 
303
- console.log('[GaeaMethods] 妯″潡宸插姞杞?- setCategoryVisible 鍙敤');
310
+ console.log('[GaeaMethods] 模块已加载 - setCategoryVisible 可用');
304
311
 
305
312
  export class GaeaApp {
306
313
  constructor(options = {}) {
@@ -309,131 +316,134 @@ export class GaeaApp {
309
316
  this.tLayers = [];
310
317
  this.layerCategories = this.config.layerCategories || [];
311
318
  this._screenIconList = [];
312
- this._markersVisible = true; // 鍏ㄥ眬鍥炬爣鍙鎬э紙2D 妯″紡涓嬪己鍒堕殣钘忥紝3D 涓嬫仮澶嶏級
319
+ this._markersVisible = true; // 全局图标可见性(2D 模式下强制隐藏,3D 下恢复)
313
320
  this._hiddenCategories = new Set();
314
- this._tilesetMap = {}; // 宸插姞杞界殑 3D Tiles 寮曠敤锛宬ey 涓?tileset 鍚嶇О
321
+ this._tilesetMap = {}; // 已加载的 3D Tiles 引用,key 为 tileset 名称
315
322
  this._secIsClickArr = [];
316
323
  this._secScreenIconKeyValue = {};
317
324
  this._groundPolygonElements = [];
318
325
  this.tweenGroup = new TWEEN.Group();
319
326
  this._tourConfig = {
320
- '娌挎郸姘撮椄': { direction: [-0.5, 0.3, 0.3], positionList: [
321
- { name: '浣嶇疆鐐?', pos: { x: 27.222747, y: 120.453035, z: 21.4 }, lockOut: [-0.5, 0.3, 0], lockOutAngle: 1.5 },
322
- { name: '浣嶇疆鐐?', pos: { x: 27.222827, y: 120.453089, z: 21.4 }, lockIn: [-0.5, 0.3, 0], lockInAngle: -1.5, lockOut: [-0.5, 0.3, 0], lockOutAngle: 1.5 },
323
- { name: '浣嶇疆鐐?', pos: { x: 27.222909, y: 120.453142, z: 21.4 }, lockIn: [-0.5, 0.3, 0], lockInAngle: -1.5, lockOut: [-0.5, 0.3, 0], lockOutAngle: 1.5 },
324
- { name: '浣嶇疆鐐?', pos: { x: 27.222982, y: 120.453192, z: 21.4 }, lockIn: [-0.5, 0.3, 0], lockInAngle: -1.5 },
327
+ '沿浦水闸': { direction: [-0.5, 0.3, 0.3], positionList: [
328
+ { name: '位置点0', pos: { x: 27.222747, y: 120.453035, z: 21.4 }, lockOut: [-0.5, 0.3, 0], lockOutAngle: 1.5 },
329
+ { name: '位置点1', pos: { x: 27.222827, y: 120.453089, z: 21.4 }, lockIn: [-0.5, 0.3, 0], lockInAngle: -1.5, lockOut: [-0.5, 0.3, 0], lockOutAngle: 1.5 },
330
+ { name: '位置点2', pos: { x: 27.222909, y: 120.453142, z: 21.4 }, lockIn: [-0.5, 0.3, 0], lockInAngle: -1.5, lockOut: [-0.5, 0.3, 0], lockOutAngle: 1.5 },
331
+ { name: '位置点3', pos: { x: 27.222982, y: 120.453192, z: 21.4 }, lockIn: [-0.5, 0.3, 0], lockInAngle: -1.5 },
325
332
  ]},
326
- '鑱旂洘姘撮椄': { direction: [1.5, 0, 0], positionList: [
327
- { name: '浣嶇疆鐐?', pos: { x: 27.200467, y: 120.465716, z: 26 }, lockIn: [0, -0.5, 0], lockInAngle: -1.5, lockOut: [0.5, 0, 0], lockOutAngle: -1.5 },
328
- { name: '浣嶇疆鐐?', pos: { x: 27.200486, y: 120.465716, z: 26 }, lockIn: [0.5, 0, 0], lockInAngle: 1.5, lockOut: [0.5, 0, 0], lockOutAngle: -1.5 },
329
- { name: '浣嶇疆鐐?', pos: { x: 27.200513, y: 120.465716, z: 26 }, lockIn: [0.5, 0, 0], lockInAngle: 1.5, lockOut: [0, 0.5, 0], lockOutAngle: -1.5 },
330
- { name: '浣嶇疆鐐?', pos: { x: 27.200513, y: 120.46561, z: 26 }, lockIn: [0, 0.5, 0], lockInAngle: 1.5, lockOut: [0, 0.5, 0], lockOutAngle: -1.5 },
331
- { name: '浣嶇疆鐐?', pos: { x: 27.200513, y: 120.465524, z: 26 }, lockIn: [0, 0.5, 0], lockInAngle: 1.5, lockOut: [-0.5, 0, 0], lockOutAngle: -1.5 },
332
- { name: '浣嶇疆鐐?', pos: { x: 27.200463, y: 120.465521, z: 26 }, lockIn: [-0.5, 0, 0], lockInAngle: 1.5, lockOut: [0, -0.5, 0], lockOutAngle: -1.5 },
333
- { name: '浣嶇疆鐐?', pos: { x: 27.200467, y: 120.465616, z: 26 }, lockIn: [0, -0.5, 0], lockInAngle: 1.5, lockOut: [0, -0.5, 0], lockOutAngle: -1.5 },
334
- { name: '浣嶇疆鐐?', pos: { x: 27.200467, y: 120.465716, z: 26 }, lockIn: [0, -0.5, 0], lockInAngle: 1.5, lockOut: [0.5, 0, 0], lockOutAngle: -1.5 },
333
+ '联盟水闸': { direction: [1.5, 0, 0], positionList: [
334
+ { name: '位置点1', pos: { x: 27.200467, y: 120.465716, z: 26 }, lockIn: [0, -0.5, 0], lockInAngle: -1.5, lockOut: [0.5, 0, 0], lockOutAngle: -1.5 },
335
+ { name: '位置点2', pos: { x: 27.200486, y: 120.465716, z: 26 }, lockIn: [0.5, 0, 0], lockInAngle: 1.5, lockOut: [0.5, 0, 0], lockOutAngle: -1.5 },
336
+ { name: '位置点3', pos: { x: 27.200513, y: 120.465716, z: 26 }, lockIn: [0.5, 0, 0], lockInAngle: 1.5, lockOut: [0, 0.5, 0], lockOutAngle: -1.5 },
337
+ { name: '位置点4', pos: { x: 27.200513, y: 120.46561, z: 26 }, lockIn: [0, 0.5, 0], lockInAngle: 1.5, lockOut: [0, 0.5, 0], lockOutAngle: -1.5 },
338
+ { name: '位置点5', pos: { x: 27.200513, y: 120.465524, z: 26 }, lockIn: [0, 0.5, 0], lockInAngle: 1.5, lockOut: [-0.5, 0, 0], lockOutAngle: -1.5 },
339
+ { name: '位置点6', pos: { x: 27.200463, y: 120.465521, z: 26 }, lockIn: [-0.5, 0, 0], lockInAngle: 1.5, lockOut: [0, -0.5, 0], lockOutAngle: -1.5 },
340
+ { name: '位置点7', pos: { x: 27.200467, y: 120.465616, z: 26 }, lockIn: [0, -0.5, 0], lockInAngle: 1.5, lockOut: [0, -0.5, 0], lockOutAngle: -1.5 },
341
+ { name: '位置点8', pos: { x: 27.200467, y: 120.465716, z: 26 }, lockIn: [0, -0.5, 0], lockInAngle: 1.5, lockOut: [0.5, 0, 0], lockOutAngle: -1.5 },
335
342
  ]},
336
- '涓嬪湪姘撮椄': { direction: [0.5, 0, 0], positionList: [
337
- { name: '浣嶇疆鐐?', pos: { x: 27.221763, y: 120.4452, z: 27.6 }, lockOut: [0, -0.5, 0], lockOutAngle: 1.5 },
338
- { name: '浣嶇疆鐐?', pos: { x: 27.221763, y: 120.445142, z: 27.6 }, lockIn: [0, -0.5, 0], lockInAngle: -1.5, lockOut: [0, -0.5, 0], lockOutAngle: 1.5 },
339
- { name: '浣嶇疆鐐?', pos: { x: 27.221763, y: 120.445084, z: 27.6 }, lockIn: [0, -0.5, 0], lockInAngle: -1.5 },
343
+ '下在水闸': { direction: [0.5, 0, 0], positionList: [
344
+ { name: '位置点1', pos: { x: 27.221763, y: 120.4452, z: 27.6 }, lockOut: [0, -0.5, 0], lockOutAngle: 1.5 },
345
+ { name: '位置点2', pos: { x: 27.221763, y: 120.445142, z: 27.6 }, lockIn: [0, -0.5, 0], lockInAngle: -1.5, lockOut: [0, -0.5, 0], lockOutAngle: 1.5 },
346
+ { name: '位置点3', pos: { x: 27.221763, y: 120.445084, z: 27.6 }, lockIn: [0, -0.5, 0], lockInAngle: -1.5 },
340
347
  ]},
341
- '宀熬鏂伴椄': { direction: [-0.5, 0.5, 0.5], positionList: [] },
342
- '宀熬姘撮椄': { direction: [-0.4, 0.5, 0.3], positionList: [] },
348
+ '岭尾新闸': { direction: [-0.5, 0.5, 0.5], positionList: [] },
349
+ '岭尾水闸': { direction: [-0.4, 0.5, 0.3], positionList: [] },
343
350
  };
344
351
  console.log('[GaeaApp] constructor layerCategories:', this.layerCategories.length);
345
352
  window.Gaea = Gaea;
346
353
 
347
- // 鏄剧ず loading
354
+ // 显示 loading
348
355
  showLoading();
349
356
 
350
- // 鑷姩鍚姩鍒濆鍖栵紝catch 闃叉鏈鐞嗙殑 Promise rejection
357
+ // 自动启动初始化,catch 防止未处理的 Promise rejection
351
358
  this.ready = this._autoInit().catch(err => {
352
- console.error('[GaeaApp] 鍒濆鍖栧け璐?', err);
359
+ console.error('[GaeaApp] 初始化失败:', err);
353
360
  });
354
361
  }
355
362
 
356
363
  get config() { return { ..._config, ...this._options }; }
357
364
 
358
- // ---- 鍐呴儴鍒濆鍖?----
365
+ // ---- 内部初始化 ----
359
366
 
360
367
  async _autoInit() {
361
- console.log('[GaeaApp] 寮€濮嬪垵濮嬪寲...');
368
+ console.log('[GaeaApp] 开始初始化...');
362
369
 
363
- // 1. 绛?canvas 灏辩华
370
+ // 1. 等 canvas 就绪
364
371
  await this._waitForCanvas();
365
- console.log('[GaeaApp] canvas 灏辩华');
372
+ console.log('[GaeaApp] canvas 就绪');
366
373
 
367
- // 2. 鍚姩 Godot 寮曟搸
374
+ // 2. 启动 Godot 引擎
368
375
  await this._startEngine();
369
- console.log('[GaeaApp] 寮曟搸鍚姩瀹屾垚');
376
+ console.log('[GaeaApp] 引擎启动完成');
370
377
 
371
- // 3. 鍒濆鍖栫幆澧? await this._initEnvironment();
372
- console.log('[GaeaApp] 鐜鍒濆鍖栧畬鎴?);
378
+ // 3. 初始化环境
379
+ await this._initEnvironment();
380
+ console.log('[GaeaApp] 环境初始化完成');
373
381
 
374
- // 4. 鍔犺浇榛樿鍥惧眰
382
+ // 4. 加载默认图层
375
383
  await this._initLayers();
376
- console.log('[GaeaApp] 鍥惧眰鍔犺浇瀹屾垚');
384
+ console.log('[GaeaApp] 图层加载完成');
377
385
 
378
- // 褰卞儚/鍦板舰灏辩华鍗冲彲鏄剧ず鍦板浘锛岄殣钘?loading锛岄伩鍏嶇敤鎴风瓑寰呮ā鍨?鎺ュ彛
386
+ // 影像/地形就绪即可显示地图,隐藏 loading,避免用户等待模型/接口
379
387
  hideLoading();
380
388
  this._addResetButton();
381
389
  this._addLayerTreeButton();
382
- console.log('[GaeaApp] 鍦板浘宸插氨缁紝妯″瀷涓庡浘灞傛爲鏁版嵁鍚庡彴鍔犺浇涓?..');
390
+ console.log('[GaeaApp] 地图已就绪,模型与图层树数据后台加载中...');
383
391
 
384
- // 鍓╀綑閲嶈祫婧愶紙妯″瀷浣嶇疆銆丟LTF銆佸浘灞傛爲鎺ュ彛銆佸睆骞曞浘鏍囷級鍚庡彴寮傛鍔犺浇锛屼笉闃诲鍦板浘鏄剧ず
392
+ // 剩余重资源(模型位置、GLTF、图层树接口、屏幕图标)后台异步加载,不阻塞地图显示
385
393
  this._loadRestAsync();
386
394
  }
387
395
 
388
- /** 鍚庡彴寮傛鍔犺浇锛氭ā鍨嬩綅缃皟鏁?鈫?GLTF 鈫?鍥惧眰鏍戞暟鎹?鈫?灞忓箷鍥炬爣 */
396
+ /** 后台异步加载:模型位置调整 → GLTF → 图层树数据 → 屏幕图标 */
389
397
  async _loadRestAsync() {
390
398
  try {
391
- // 5. 璋冩暣妯″瀷浣嶇疆
392
- console.log('[GaeaApp] 寮€濮嬭皟鏁存ā鍨嬩綅缃?..');
399
+ // 5. 调整模型位置
400
+ console.log('[GaeaApp] 开始调整模型位置...');
393
401
  const models = [
394
- { nodeName: '娌挎郸姘撮椄', position: '-2876603.788, 4892658.352, 2900208.622', rotation: '-37.24, 162.42, -17.17' },
395
- { nodeName: '涓嬪湪姘撮椄', position: '-2875959.024, 4893109.058, 2900101.919', rotation: '-23.42, -165.20, -34.52' },
396
- { nodeName: '鑱旂洘姘撮椄', position: '-2878251.995, 4893011.199, 2897998.160', rotation: '-23.43, -165.98, -33.40' },
397
- { nodeName: '宀熬鏂伴椄', position: '-2877382.212, 4892920.226, 2898965.484', rotation: '7.79, 36.98, 40.83' },
398
- { nodeName: '宀熬姘撮椄', position: '-2877582.576, 4892520.352, 2899499.184', rotation: '-41.04, 126.37, 6.29' },
402
+ { nodeName: '沿浦水闸', position: '-2876603.788, 4892658.352, 2900208.622', rotation: '-37.24, 162.42, -17.17' },
403
+ { nodeName: '下在水闸', position: '-2875959.024, 4893109.058, 2900101.919', rotation: '-23.42, -165.20, -34.52' },
404
+ { nodeName: '联盟水闸', position: '-2878251.995, 4893011.199, 2897998.160', rotation: '-23.43, -165.98, -33.40' },
405
+ { nodeName: '岭尾新闸', position: '-2877382.212, 4892920.226, 2898965.484', rotation: '7.79, 36.98, 40.83' },
406
+ { nodeName: '岭尾水闸', position: '-2877582.576, 4892520.352, 2899499.184', rotation: '-41.04, 126.37, 6.29' },
399
407
  ];
400
408
  for (const model of models) {
401
409
  try {
402
410
  const node = Gaea.World.Instance.GetNode(model.nodeName);
403
- if (!node) { console.warn(`[GaeaApp] 鏈壘鍒拌妭鐐? ${model.nodeName}`); continue; }
411
+ if (!node) { console.warn(`[GaeaApp] 未找到节点: ${model.nodeName}`); continue; }
404
412
  const inst = Gaea.MeshInstance.ConvertBy(node);
405
413
  const pos = model.position.split(',').map(v => parseFloat(v.trim()));
406
414
  const rot = model.rotation.split(',').map(v => parseFloat(v.trim()));
407
415
  if (inst.Translation) inst.Translation = new Gaea.Vector3(pos[0], pos[1], pos[2]);
408
416
  if (inst.RotationDegrees) inst.RotationDegrees = new Gaea.Vector3(rot[0], rot[1], rot[2]);
409
417
  } catch (e) {
410
- console.warn(`[GaeaApp] 璋冩暣妯″瀷 ${model.nodeName} 澶辫触:`, e.message);
418
+ console.warn(`[GaeaApp] 调整模型 ${model.nodeName} 失败:`, e.message);
411
419
  }
412
420
  }
413
- console.log('[GaeaApp] 鎵€鏈夋ā鍨嬩綅缃皟鏁村畬鎴?);
421
+ console.log('[GaeaApp] 所有模型位置调整完成');
414
422
 
415
- // 6. 鍔犺浇 GLTF 妯″瀷
423
+ // 6. 加载 GLTF 模型
416
424
  await this._loadGltfModels();
417
425
 
418
- // 7. 鑾峰彇鍥惧眰鏍戞暟鎹紙鏈夋帴鍙h蛋鎺ュ彛锛屾病鎺ュ彛鐢ㄩ粯璁わ級
426
+ // 7. 获取图层树数据(有接口走接口,没接口用默认)
419
427
  await this._fetchLayerTreeData();
420
428
 
421
- // 鍚屾 layerCategories锛堜慨澶嶆暟鎹洿鏂版湭鍚屾鐨勯棶棰橈級
429
+ // 同步 layerCategories(修复数据更新未同步的问题)
422
430
  this.layerCategories = this.config.layerCategories || [];
423
- console.log('[GaeaApp] layerCategories 鏇存柊鍚?', this.layerCategories.length);
431
+ console.log('[GaeaApp] layerCategories 更新后:', this.layerCategories.length);
424
432
 
425
- // 8. 鍒濆鍖栧睆骞曞浘鏍? this._initScreenIcons();
433
+ // 8. 初始化屏幕图标
434
+ this._initScreenIcons();
426
435
 
427
- // 搴旂敤榛樿闅愯棌鐨勫垎绫? const hiddenCats = this.config.defaultHiddenCategories || []
436
+ // 应用默认隐藏的分类
437
+ const hiddenCats = this.config.defaultHiddenCategories || []
428
438
  hiddenCats.forEach(key => {
429
439
  const cat = this.layerCategories.find(c => c.key === key)
430
440
  if (cat) cat.visible = false
431
441
  this.setCategoryVisible(key, false)
432
442
  })
433
443
 
434
- console.log('[GaeaApp] 鍚庡彴璧勬簮鍔犺浇瀹屾垚');
444
+ console.log('[GaeaApp] 后台资源加载完成');
435
445
  } catch (e) {
436
- console.error('[GaeaApp] 鍚庡彴璧勬簮鍔犺浇鍑洪敊:', e);
446
+ console.error('[GaeaApp] 后台资源加载出错:', e);
437
447
  }
438
448
  }
439
449
 
@@ -445,7 +455,7 @@ export class GaeaApp {
445
455
  if (el && el.width > 0) {
446
456
  resolve();
447
457
  } else if (Date.now() - start > 10000) {
448
- reject(new Error('绛夊緟 canvas 瓒呮椂'));
458
+ reject(new Error('等待 canvas 超时'));
449
459
  } else {
450
460
  requestAnimationFrame(check);
451
461
  }
@@ -456,10 +466,10 @@ export class GaeaApp {
456
466
 
457
467
  async _startEngine() {
458
468
  if (!window.Engine) {
459
- throw new Error('window.Engine 涓嶅瓨鍦紝璇风‘淇?<script src="/Gaea.js"> 宸插姞杞?);
469
+ throw new Error('window.Engine 不存在,请确保 <script src="/Gaea.js"> 已加载');
460
470
  }
461
471
  if (!window.Engine.isWebGLAvailable()) {
462
- throw new Error('WebGL 涓嶅彲鐢?);
472
+ throw new Error('WebGL 不可用');
463
473
  }
464
474
 
465
475
  const cfg = this.config;
@@ -476,7 +486,7 @@ export class GaeaApp {
476
486
  });
477
487
 
478
488
  await engine.startGame({
479
- onProgress: cfg.onProgress || ((cur, total) => console.log(`寮曟搸鍔犺浇: ${cur}/${total}`)),
489
+ onProgress: cfg.onProgress || ((cur, total) => console.log(`引擎加载: ${cur}/${total}`)),
480
490
  });
481
491
  }
482
492
 
@@ -505,10 +515,10 @@ export class GaeaApp {
505
515
  const cfg = this.config;
506
516
 
507
517
  if (cfg.serviceMode === 'online') {
508
- // 鍦ㄧ嚎妯″紡锛氫粎鍔犺浇澶╁湴鍥惧奖鍍忥紝涓嶅姞杞藉湴褰?璐村湴绾?3D Tiles
518
+ // 在线模式:仅加载天地图影像,不加载地形/贴地线/3D Tiles
509
519
  this.addTianditu();
510
520
  } else {
511
- // 鏈嶅姟骞冲彴妯″紡锛歐MTS 褰卞儚 + 璐村湴绾?+ 鍦板舰
521
+ // 服务平台模式:WMTS 影像 + 贴地线 + 地形
512
522
  await this.getAllServe();
513
523
  const list = cfg.defaultLayers;
514
524
  if (list?.length) {
@@ -516,74 +526,79 @@ export class GaeaApp {
516
526
  this.addLayer(layer);
517
527
  }
518
528
  }
519
- // 璐村湴绾匡紙鐭㈤噺绾挎湇鍔★紝渚濊禆 this.layers锛? try { this.addGroundLines(); } catch (e) { console.error('鍔犺浇璐村湴绾垮け璐?', e); }
520
- // 鍦板舰鏈嶅姟
521
- try { await this.addInitTerrainServer(); } catch (e) { console.error('鍔犺浇鍦板舰澶辫触:', e); }
522
- // 鍊炬枩鎽勫奖 3D Tiles锛坕sInitLoad 涓?true 鐨勮嚜鍔ㄥ姞杞斤紝渚濊禆鍦板舰鏈嶅姟锛? const tiles = cfg.default3DTiles || []
529
+ // 贴地线(矢量线服务,依赖 this.layers)
530
+ try { this.addGroundLines(); } catch (e) { console.error('加载贴地线失败:', e); }
531
+ // 地形服务
532
+ try { await this.addInitTerrainServer(); } catch (e) { console.error('加载地形失败:', e); }
533
+ // 倾斜摄影 3D Tiles(isInitLoad 为 true 的自动加载,依赖地形服务)
534
+ const tiles = cfg.default3DTiles || []
523
535
  tiles.filter(item => item.isInitLoad).forEach(item => this.add3DTiles(item))
524
536
  }
525
537
 
526
538
  const pos = cfg.defaultCameraPosition;
527
539
  if (pos) {
528
- // 绛変竴甯у啀璁剧浉鏈猴紝纭繚寮曟搸鎶婂浘灞傚鐞嗗畬
540
+ // 等一帧再设相机,确保引擎把图层处理完
529
541
  await new Promise(r => requestAnimationFrame(r));
530
542
  this.SetPosition(pos);
531
543
  }
532
544
  }
533
545
 
534
- // ---- 澶╁湴鍥?----
546
+ // ---- 天地图 ----
535
547
 
536
- /** 鍔犺浇澶╁湴鍥惧奖鍍?+ 娉ㄨ */
548
+ /** 加载天地图影像 + 注记 */
537
549
  addTianditu() {
538
550
  const cfg = this.config;
539
551
 
540
- // 褰卞儚搴曞浘锛堝崼鏄熷浘锛? const imgStore = new Gaea.TDTWMTSImageStore();
552
+ // 影像底图(卫星图)
553
+ const imgStore = new Gaea.TDTWMTSImageStore();
541
554
  imgStore.ServerUri = cfg.tiandituServerUri || 'https://t{0}.tianditu.gov.cn/img_c/wmts';
542
555
  imgStore.FormatString = cfg.tiandituFormatString || 'SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=c&FORMAT=tiles&TILECOL={0}&TILEROW={1}&TILEMATRIX={2}&tk={3}';
543
556
  imgStore.ImageExtension = cfg.tiandituImageExtension ?? Gaea.ImageExtensionEnum.JPEG;
544
557
  imgStore.TK = cfg.tiandituToken;
545
- imgStore.Name = '褰卞儚搴曞浘';
558
+ imgStore.Name = '影像底图';
546
559
  imgStore.LevelRange = new Gaea.Vector2(cfg.tiandituLevelRange[0], cfg.tiandituLevelRange[1]);
547
560
  Gaea.World.Instance.DefaultPyramidTileSet.AddImageStore(imgStore);
548
- console.log('澶╁湴鍥惧奖鍍忓姞杞芥垚鍔?);
561
+ console.log('天地图影像加载成功');
549
562
 
550
- // 娉ㄨ鍥惧眰锛堥亾璺?鍦板悕鏍囨敞锛? const labelStore = new Gaea.TDTWMTSImageStore();
563
+ // 注记图层(道路/地名标注)
564
+ const labelStore = new Gaea.TDTWMTSImageStore();
551
565
  labelStore.ServerUri = 'https://t{0}.tianditu.gov.cn/cia_c/wmts';
552
566
  labelStore.FormatString = 'SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cia&STYLE=default&TILEMATRIXSET=c&FORMAT=image/png&TILECOL={0}&TILEROW={1}&TILEMATRIX={2}&tk={3}';
553
567
  labelStore.ImageExtension = Gaea.ImageExtensionEnum.PNG;
554
568
  labelStore.TK = cfg.tiandituToken;
555
- labelStore.Name = '澶╁湴鍥炬敞璁?;
569
+ labelStore.Name = '天地图注记';
556
570
  labelStore.LevelRange = new Gaea.Vector2(cfg.tiandituLevelRange[0], cfg.tiandituLevelRange[1]);
557
571
  Gaea.World.Instance.DefaultPyramidTileSet.AddImageStore(labelStore);
558
- console.log('澶╁湴鍥炬敞璁板姞杞芥垚鍔?);
572
+ console.log('天地图注记加载成功');
559
573
 
560
574
  return { imgStore, labelStore };
561
575
  }
562
576
 
563
- /** 娣诲姞鍙充笂瑙掗噸缃寜閽?*/
577
+ /** 添加右上角重置按钮 */
564
578
  _addResetButton() {
565
579
  if (document.getElementById('gaea-reset-btn')) return;
566
580
  const btn = document.createElement('button');
567
581
  btn.id = 'gaea-reset-btn';
568
- btn.title = '閲嶇疆瑙嗚';
569
- btn.innerHTML = '鉄?;
582
+ btn.title = '重置视角';
583
+ btn.innerHTML = '⟳';
570
584
  btn.onclick = () => {
571
585
  const pos = this.config.defaultCameraPosition;
572
586
  if (pos) this.SetPosition(pos);
573
- // 瑙i攣瑙嗚锛堝唴閮ㄤ細璋?UseAutoVisible 鎭㈠闈炴柇闈㈠浘鏍囷級
587
+ // 解锁视角(内部会调 UseAutoVisible 恢复非断面图标)
574
588
  this.lockedModesSwitch(false);
575
- // 鎭㈠琚?hiddenScreenWithClass 闅愯棌鐨勫浘鏍?DOM
589
+ // 恢复被 hiddenScreenWithClass 隐藏的图标 DOM
576
590
  if (this._markersVisible) {
577
591
  document.querySelectorAll('.gaea-locationLineStyle, .gaea-iconscreen, .gaea-iconGif').forEach(el => {
578
592
  el.style.display = '';
579
593
  });
580
594
  }
581
- // 鏂潰妯″瀷缂╁洖
595
+ // 断面模型缩回
582
596
  if (this._raisedSection) {
583
597
  this._raisedSection.TranslateObjectLocal(new Gaea.Vector3(0, -44, 0));
584
598
  this._raisedSection = null;
585
599
  }
586
- // 閲嶇疆娓歌妯″瀷浣嶇疆鍒板垵濮嬩綅缃? if (this._currentTourModel) {
600
+ // 重置游览模型位置到初始位置
601
+ if (this._currentTourModel) {
587
602
  const name = this._currentTourModel?.Root?.Name;
588
603
  const cfg = this._gltfCfgMap?.[name];
589
604
  if (cfg) {
@@ -592,9 +607,10 @@ export class GaeaApp {
592
607
  this._currentTourModel = null;
593
608
  this._currentTourIndexPos = 0;
594
609
  }
595
- // 閲嶇疆鏂潰鐘舵€? this._secIsClickArr = [];
610
+ // 重置断面状态
611
+ this._secIsClickArr = [];
596
612
  this._secScreenIconKeyValue = {};
597
- // 闅愯棌鏂潰鏍囩
613
+ // 隐藏断面标签
598
614
  this._screenIconList.forEach(item => {
599
615
  if (item.type === 'dabaSec') {
600
616
  item.element.ForceSetVisible(false);
@@ -602,48 +618,48 @@ export class GaeaApp {
602
618
  }
603
619
  });
604
620
  document.querySelectorAll('.waterLevel, .damScreen').forEach(el => el.style.display = 'none');
605
- // 閫氱煡 SecTourView 闅愯棌
621
+ // 通知 SecTourView 隐藏
606
622
  document.dispatchEvent(new CustomEvent('gaea-reset-view', { bubbles: true }));
607
623
  };
608
624
  document.body.appendChild(btn);
609
625
  }
610
626
 
611
- /** 娣诲姞鍥惧眰鏍戞寜閽?*/
627
+ /** 添加图层树按钮 */
612
628
  _addLayerTreeButton() {
613
629
  if (document.getElementById('gaea-layertree-btn')) return
614
630
  const btn = document.createElement('button')
615
631
  btn.id = 'gaea-layertree-btn'
616
- btn.title = '鍥惧眰鎺у埗'
617
- btn.innerHTML = '鈽?
632
+ btn.title = '图层控制'
633
+ btn.innerHTML = '☰'
618
634
  btn.onclick = () => {
619
635
  document.dispatchEvent(new CustomEvent('gaea-toggle-layertree'))
620
636
  }
621
637
  document.body.appendChild(btn)
622
638
  }
623
639
 
624
- // ---- GLTF 妯″瀷 ----
640
+ // ---- GLTF 模型 ----
625
641
 
626
642
  async _loadGltfModels() {
627
643
  const gltfList = [
628
- { name: 'YPDP0+600', position: { x: 27.217692, y: 120.461823, z: -44 }, rotate: [0, 198, 0], size: [1, 1, 1], show: true, url: 'gltf/澶у潩鏂潰600.gltf' },
629
- { name: 'YPDP0+750', position: { x: 27.220289, y: 120.459753, z: -44 }, rotate: [0, 226, 0], size: [1, 1, 1], show: true, url: 'gltf/澶у潩鏂潰750.gltf' },
630
- { name: 'YPDP0+100', position: { x: 27.223027, y: 120.454653, z: -44 }, rotate: [0, 222, 0], size: [1, 1, 1], show: true, url: 'gltf/澶у潩鏂潰100.gltf' },
631
- { name: 'YPDP0+200', position: { x: 27.222541, y: 120.455453, z: -44 }, rotate: [0, 246, 0], size: [1, 1, 1], show: true, url: 'gltf/澶у潩鏂潰200.gltf' },
632
- { name: '娌挎郸娉电珯', position: { x: 27.222001, y: 120.452509, z: 17 }, rotate: [0, -38, 0], size: [1, 1, 1], show: true, url: 'gltf/娌挎郸娉电珯.gltf', enablePick: true },
633
- { name: '涓嬪湪娴峰绾虫帓闂?, position: { x: 27.219978, y: 120.445672, z: 15 }, rotate: [0, 0, 0], size: [1, 1, 1], show: true, url: 'gltf/涓嬪湪娴峰绾虫帓闂?gltf', enablePick: true },
634
- { name: '鐩掑瓙妯″瀷', position: { x: 27.241836, y: 120.462750, z: 50 }, rotate: [0, 90, 0], size: [10, 10, 10], show: true, url: 'gltf/box.gltf' },
635
- { name: 'YPDP0+600娓歌妯″瀷', position: { x: 27.217433, y: 120.461448, z: 32 }, rotate: [0, 90, 0], size: [1, 1, 1], show: true, url: 'gltf/box.gltf' },
636
- { name: 'YPDP0+750娓歌妯″瀷', position: { x: 27.220072, y: 120.459602, z: 30 }, rotate: [0, 90, 0], size: [1, 1, 1], show: true, url: 'gltf/box.gltf' },
637
- { name: 'YPDP0+100娓歌妯″瀷', position: { x: 27.222844, y: 120.454466, z: 30 }, rotate: [0, 90, 0], size: [1, 1, 1], show: true, url: 'gltf/box.gltf' },
638
- { name: 'YPDP0+200娓歌妯″瀷', position: { x: 27.22226, y: 120.455318, z: 30 }, rotate: [0, 90, 0], size: [1, 1, 1], show: true, url: 'gltf/box.gltf' },
639
- { name: '娌挎郸姘撮椄娓歌妯″瀷', position: { x: 27.222747, y: 120.453035, z: 21.8 }, rotate: [0, -30, 0], size: [1, 1, 1], show: false, url: 'gltf/box.gltf' },
640
- { name: '娌挎郸姘撮椄瀹氫綅妯″瀷', position: { x: 27.230788, y: 120.453009, z: 487.5 }, rotate: [0, 0, 0], size: [1, 1, 1], show: false, url: 'gltf/box.gltf' },
641
- { name: '涓嬪湪姘撮椄娓歌妯″瀷', position: { x: 27.221763, y: 120.4452, z: 27.6 }, rotate: [0, 0, 0], size: [1, 1, 1], show: false, url: 'gltf/box.gltf' },
642
- { name: '鑱旂洘姘撮椄娓歌妯″瀷', position: { x: 27.200467, y: 120.465716, z: 26 }, rotate: [0, 180, 0], size: [0.1, 0.1, 0.1], show: false, url: 'gltf/box.gltf' },
643
- { name: '宀熬姘撮椄娓歌妯″瀷', position: { x: 27.215661, y: 120.462325, z: 34 }, rotate: [0, -66, 0], size: [1, 1, 1], show: false, url: 'gltf/box.gltf' },
644
- { name: '宀熬鏂伴椄娓歌妯″瀷', position: { x: 27.21038, y: 120.458513, z: 4 }, rotate: [0, 30, 0], size: [1, 1, 1], show: false, url: 'gltf/box.gltf' },
645
- { name: '鐩掑瓙妯″瀷-yanpu', position: { x: 27.231665, y: 120.456095, z: 48.608 }, rotate: [0, 90, 0], size: [10, 10, 10], show: true, url: 'gltf/box.gltf' },
646
- { name: '闃叉睕娌欒', position: { x: 27.218723, y: 120.460473, z: 18 }, rotate: [0, 0, 0], size: [1, 1, 1], show: false, url: 'gltf/sanddai.glb' },
644
+ { name: 'YPDP0+600', position: { x: 27.217692, y: 120.461823, z: -44 }, rotate: [0, 198, 0], size: [1, 1, 1], show: true, url: 'gltf/大坝断面600.gltf' },
645
+ { name: 'YPDP0+750', position: { x: 27.220289, y: 120.459753, z: -44 }, rotate: [0, 226, 0], size: [1, 1, 1], show: true, url: 'gltf/大坝断面750.gltf' },
646
+ { name: 'YPDP0+100', position: { x: 27.223027, y: 120.454653, z: -44 }, rotate: [0, 222, 0], size: [1, 1, 1], show: true, url: 'gltf/大坝断面100.gltf' },
647
+ { name: 'YPDP0+200', position: { x: 27.222541, y: 120.455453, z: -44 }, rotate: [0, 246, 0], size: [1, 1, 1], show: true, url: 'gltf/大坝断面200.gltf' },
648
+ { name: '沿浦泵站', position: { x: 27.222001, y: 120.452509, z: 17 }, rotate: [0, -38, 0], size: [1, 1, 1], show: true, url: 'gltf/沿浦泵站.gltf', enablePick: true },
649
+ { name: '下在海塘纳排闸', position: { x: 27.219978, y: 120.445672, z: 15 }, rotate: [0, 0, 0], size: [1, 1, 1], show: true, url: 'gltf/下在海塘纳排闸.gltf', enablePick: true },
650
+ { name: '盒子模型', position: { x: 27.241836, y: 120.462750, z: 50 }, rotate: [0, 90, 0], size: [10, 10, 10], show: true, url: 'gltf/box.gltf' },
651
+ { name: 'YPDP0+600游览模型', position: { x: 27.217433, y: 120.461448, z: 32 }, rotate: [0, 90, 0], size: [1, 1, 1], show: true, url: 'gltf/box.gltf' },
652
+ { name: 'YPDP0+750游览模型', position: { x: 27.220072, y: 120.459602, z: 30 }, rotate: [0, 90, 0], size: [1, 1, 1], show: true, url: 'gltf/box.gltf' },
653
+ { name: 'YPDP0+100游览模型', position: { x: 27.222844, y: 120.454466, z: 30 }, rotate: [0, 90, 0], size: [1, 1, 1], show: true, url: 'gltf/box.gltf' },
654
+ { name: 'YPDP0+200游览模型', position: { x: 27.22226, y: 120.455318, z: 30 }, rotate: [0, 90, 0], size: [1, 1, 1], show: true, url: 'gltf/box.gltf' },
655
+ { name: '沿浦水闸游览模型', position: { x: 27.222747, y: 120.453035, z: 21.8 }, rotate: [0, -30, 0], size: [1, 1, 1], show: false, url: 'gltf/box.gltf' },
656
+ { name: '沿浦水闸定位模型', position: { x: 27.230788, y: 120.453009, z: 487.5 }, rotate: [0, 0, 0], size: [1, 1, 1], show: false, url: 'gltf/box.gltf' },
657
+ { name: '下在水闸游览模型', position: { x: 27.221763, y: 120.4452, z: 27.6 }, rotate: [0, 0, 0], size: [1, 1, 1], show: false, url: 'gltf/box.gltf' },
658
+ { name: '联盟水闸游览模型', position: { x: 27.200467, y: 120.465716, z: 26 }, rotate: [0, 180, 0], size: [0.1, 0.1, 0.1], show: false, url: 'gltf/box.gltf' },
659
+ { name: '岭尾水闸游览模型', position: { x: 27.215661, y: 120.462325, z: 34 }, rotate: [0, -66, 0], size: [1, 1, 1], show: false, url: 'gltf/box.gltf' },
660
+ { name: '岭尾新闸游览模型', position: { x: 27.21038, y: 120.458513, z: 4 }, rotate: [0, 30, 0], size: [1, 1, 1], show: false, url: 'gltf/box.gltf' },
661
+ { name: '盒子模型-yanpu', position: { x: 27.231665, y: 120.456095, z: 48.608 }, rotate: [0, 90, 0], size: [10, 10, 10], show: true, url: 'gltf/box.gltf' },
662
+ { name: '防汛沙袋', position: { x: 27.218723, y: 120.460473, z: 18 }, rotate: [0, 0, 0], size: [1, 1, 1], show: false, url: 'gltf/sanddai.glb' },
647
663
  ];
648
664
 
649
665
  this._gltfCfgMap = {};
@@ -652,15 +668,16 @@ export class GaeaApp {
652
668
  this._gltfCfgMap[item.name] = item;
653
669
  }
654
670
 
655
- console.log('[GaeaApp] 寮€濮嬪姞杞?GLTF 妯″瀷...');
656
- // 骞惰鍔犺浇鎵€鏈夋ā鍨嬶紙鍘熶覆琛岄€愪釜 await 浼氱疮鍔犺€楁椂锛? await Promise.all(gltfList.map(async (item) => {
671
+ console.log('[GaeaApp] 开始加载 GLTF 模型...');
672
+ // 并行加载所有模型(原串行逐个 await 会累加耗时)
673
+ await Promise.all(gltfList.map(async (item) => {
657
674
  item.url = encodeURI(item.url);
658
675
  try {
659
676
  const el = await this.AddGltf(item);
660
677
  if (el) this._gltfElements[item.name] = el;
661
- } catch (e) { console.warn(`[GaeaApp] 妯″瀷 ${item.name} 鍔犺浇澶辫触:`, e.message); }
678
+ } catch (e) { console.warn(`[GaeaApp] 模型 ${item.name} 加载失败:`, e.message); }
662
679
  }));
663
- console.log('[GaeaApp] GLTF 妯″瀷鍔犺浇瀹屾垚');
680
+ console.log('[GaeaApp] GLTF 模型加载完成');
664
681
  }
665
682
 
666
683
  AddGltf = ({
@@ -672,10 +689,10 @@ export class GaeaApp {
672
689
  const element = new Gaea.GLTFElement();
673
690
  element.UseAbsoluteHeight = useAbsoluteHeight;
674
691
  element.ResourceName = url;
675
- console.log(`妯″瀷寮€濮嬪姞杞? ${name}`);
692
+ console.log(`模型开始加载: ${name}`);
676
693
 
677
694
  element.OnModelInit(async () => {
678
- // 澶у潩鏂潰妯″瀷璁剧疆 Unshaded
695
+ // 大坝断面模型设置 Unshaded
679
696
  if (name.includes('YPDP0')) {
680
697
  const n = element.Root.GetChildCount();
681
698
  for (let i = 0; i < n; i++) {
@@ -687,7 +704,7 @@ export class GaeaApp {
687
704
  }
688
705
  }
689
706
  }
690
- console.log(`妯″瀷鍔犺浇瀹屾瘯: ${name}`);
707
+ console.log(`模型加载完毕: ${name}`);
691
708
  initFun();
692
709
  element.EnablePick = enablePick;
693
710
  Gaea.QueryServer.Instance.Enable = enablePick;
@@ -706,35 +723,36 @@ export class GaeaApp {
706
723
  });
707
724
  };
708
725
 
709
- // ---- 鍥惧眰鏍戞暟鎹?----
726
+ // ---- 图层树数据 ----
710
727
 
711
728
  async _fetchLayerTreeData() {
712
729
  const cfg = this.config;
713
730
  const apiUrl = cfg.dataHubApiUrl;
714
731
  if (!apiUrl) {
715
- console.log('[GaeaApp] 鏈厤缃?dataHubApiUrl锛屼娇鐢ㄩ粯璁ゅ浘灞傛爲鏁版嵁');
732
+ console.log('[GaeaApp] 未配置 dataHubApiUrl,使用默认图层树数据');
716
733
  return;
717
734
  }
718
- console.log('[GaeaApp] 浠?DataHub 鑾峰彇鍥惧眰鏍戞暟鎹?..');
735
+ console.log('[GaeaApp] 从 DataHub 获取图层树数据...');
719
736
  try {
720
737
  const res = await fetch(`${apiUrl}/open/api/5rLGPS6E8Zvr`, { headers: this._dataHubHeaders(cfg) });
721
738
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
722
739
  const json = await res.json();
723
740
  if (json.data) this._mergeStationData(json.data);
724
- console.log('[GaeaApp] 鍥惧眰鏍戞暟鎹洿鏂板畬鎴?);
741
+ console.log('[GaeaApp] 图层树数据更新完成');
725
742
  } catch (e) {
726
- console.warn('[GaeaApp] 鎺ュ彛鑾峰彇澶辫触锛屼娇鐢ㄩ粯璁ゆ暟鎹?', e.message);
743
+ console.warn('[GaeaApp] 接口获取失败,使用默认数据:', e.message);
727
744
  }
728
745
  }
729
746
 
730
- /** DataHub API 绛惧悕澶达紙HmacSHA256锛?*/
747
+ /** DataHub API 签名头(HmacSHA256) */
731
748
  _dataHubHeaders(cfg) {
732
749
  const appKey = cfg.dataHubAppKey || 'testAppKey';
733
750
  const appSecret = cfg.dataHubAppSecret || 'testAppSecret';
734
751
  const nonce = Math.random().toString(36).substring(2, 10);
735
752
  const timestamp = Date.now();
736
753
  const signStr = `${appKey}${timestamp}${nonce}`;
737
- // 绠€鍖栫増锛氱敤 btoa 鏇夸唬 HmacSHA256锛堢敓浜х幆澧冮渶鐢?Web Crypto API锛? const signature = btoa(signStr + appSecret);
754
+ // 简化版:用 btoa 替代 HmacSHA256(生产环境需用 Web Crypto API)
755
+ const signature = btoa(signStr + appSecret);
738
756
  return {
739
757
  'Content-Type': 'application/json',
740
758
  'X-App-Key': appKey,
@@ -744,7 +762,7 @@ export class GaeaApp {
744
762
  };
745
763
  }
746
764
 
747
- /** 灏咥PI娴嬬珯鏁版嵁鍚堝苟鍒伴粯璁ゅ垎绫讳腑 */
765
+ /** 将API测站数据合并到默认分类中 */
748
766
  _mergeStationData(apiData) {
749
767
  const cfg = this.config;
750
768
  const sttpMap = {
@@ -770,12 +788,12 @@ export class GaeaApp {
770
788
  }
771
789
  }
772
790
 
773
- /** 浠庡浘灞傛爲鏁版嵁鍒涘缓灞忓箷鍥炬爣 */
791
+ /** 从图层树数据创建屏幕图标 */
774
792
  _initScreenIcons() {
775
793
  const cats = this.layerCategories || [];
776
794
  const catConfig = {
777
795
  seawall: {
778
- img: '/image/screenIcon/娴峰-绾?png', method: 'textImage',
796
+ img: '/image/screenIcon/海塘-红.png', method: 'textImage',
779
797
  visibleRange: [5000, 150000], offset: [-40, -35], anchorType: 6,
780
798
  onClick: (name, data) => {
781
799
  this.flyToNode(data);
@@ -783,7 +801,7 @@ export class GaeaApp {
783
801
  },
784
802
  },
785
803
  hydrometricStations: {
786
- img: '/image/screenIcon/娼綅绔?姝e父.png', method: 'textImage',
804
+ img: '/image/screenIcon/潮位站-正常.png', method: 'textImage',
787
805
  visibleRange: [10, 5000], offset: [-40, -35],
788
806
  onClick: (name, data) => {
789
807
  console.log('[GaeaApp] hydrometricStations onClick:', name, data);
@@ -796,18 +814,18 @@ export class GaeaApp {
796
814
  if (window.__gaeaIconClickHandler) {
797
815
  window.__gaeaIconClickHandler({ detail: eventData });
798
816
  }
799
- console.log('[GaeaApp] hydrometricStations 浜嬩欢宸叉淳鍙?);
817
+ console.log('[GaeaApp] hydrometricStations 事件已派发');
800
818
  },
801
819
  },
802
820
  tideStations: {
803
- img: '/image/screenIcon/娼綅绔?姝e父.png', method: 'textImage',
821
+ img: '/image/screenIcon/潮位站-正常.png', method: 'textImage',
804
822
  visibleRange: [10, 5000], offset: [-40, -35],
805
823
  onClick: (name, data) => {
806
824
  document.dispatchEvent(new CustomEvent('gaea-icon-click', { detail: { category: 'tideStations', name, data } }));
807
825
  },
808
826
  },
809
827
  riverStations: {
810
- img: '/image/screenIcon/娌抽亾绔?姝e父.png', method: 'textImage',
828
+ img: '/image/screenIcon/河道站-正常.png', method: 'textImage',
811
829
  visibleRange: [10, 5000], offset: [-40, -35],
812
830
  onClick: (name, data) => {
813
831
  this.flyToNode(data);
@@ -815,7 +833,7 @@ export class GaeaApp {
815
833
  },
816
834
  },
817
835
  rainStations: {
818
- img: '/image/screenIcon/闆ㄩ噺绔?缁?png', method: 'textImage',
836
+ img: '/image/screenIcon/雨量站-绿.png', method: 'textImage',
819
837
  visibleRange: [10, 5000], offset: [-40, -35],
820
838
  onClick: (name, data) => {
821
839
  this.flyToNode(data);
@@ -823,7 +841,7 @@ export class GaeaApp {
823
841
  },
824
842
  },
825
843
  reservoirs: {
826
- img: '/image/screenIcon/姘村簱绔?png', method: 'styled',
844
+ img: '/image/screenIcon/水库站.png', method: 'styled',
827
845
  visibleRange: [10, 5000], offset: [-40, -35],
828
846
  onClick: (name, data) => {
829
847
  this.flyToNode(data);
@@ -831,7 +849,7 @@ export class GaeaApp {
831
849
  },
832
850
  },
833
851
  sluice: {
834
- img: '/image/screenIcon/闂搁棬-缁?png', method: 'textImage',
852
+ img: '/image/screenIcon/闸门-绿.png', method: 'textImage',
835
853
  visibleRange: [10, 3000], offset: [-40, -35],
836
854
  onClick: (name, data) => {
837
855
  this.flyToNode(data);
@@ -839,21 +857,21 @@ export class GaeaApp {
839
857
  },
840
858
  },
841
859
  secData: {
842
- img: '/image/screenIcon/鏂潰.png', method: 'styled',
860
+ img: '/image/screenIcon/断面.png', method: 'styled',
843
861
  visibleRange: [10, 3000], offset: [-40, -35],
844
862
  onClick: (name, data) => {
845
863
  this._onSecDataClick(name, data);
846
864
  },
847
865
  },
848
866
  uavNests: {
849
- img: '/image/screenIcon/鏃犱汉鏈?png', method: 'imageOnly',
867
+ img: '/image/screenIcon/无人机.png', method: 'imageOnly',
850
868
  visibleRange: [10, 5000], offset: [-25, -80],
851
869
  onClick: (name, data) => {
852
870
  document.dispatchEvent(new CustomEvent('gaea-icon-click', { detail: { category: 'uavNests', name, data } }));
853
871
  },
854
872
  },
855
873
  rescue: {
856
- img: '/image/screenIcon/闅愭偅.png', method: 'imageOnly',
874
+ img: '/image/screenIcon/隐患.png', method: 'imageOnly',
857
875
  visibleRange: [10, 5000], offset: [-25, -80],
858
876
  onClick: (name, data) => {
859
877
  this.flyToNode(data);
@@ -861,7 +879,7 @@ export class GaeaApp {
861
879
  },
862
880
  },
863
881
  materials: {
864
- img: '/image/screenIcon/鐗╄祫浠撳簱.png', method: 'imageOnly',
882
+ img: '/image/screenIcon/物资仓库.png', method: 'imageOnly',
865
883
  visibleRange: [10, 5000], offset: [-25, -80],
866
884
  onClick: (name, data) => {
867
885
  this.flyToNode(data);
@@ -869,7 +887,7 @@ export class GaeaApp {
869
887
  },
870
888
  },
871
889
  };
872
- console.log('[GaeaApp] 寮€濮嬪垱寤哄睆骞曞浘鏍?.. categories:', cats.length);
890
+ console.log('[GaeaApp] 开始创建屏幕图标... categories:', cats.length);
873
891
  let count = 0;
874
892
  for (const cat of cats) {
875
893
  if (!cat.children?.length) continue;
@@ -894,34 +912,38 @@ export class GaeaApp {
894
912
  },
895
913
  });
896
914
  } catch (e) {
897
- console.warn(`[GaeaApp] 鍥炬爣 ${child.text} 鍒涘缓澶辫触:`, e.message);
915
+ console.warn(`[GaeaApp] 图标 ${child.text} 创建失败:`, e.message);
898
916
  }
899
917
  }
900
- // 鏍规嵁閰嶇疆鐨?visible 鍐冲畾鍒濆鏄鹃殣锛堜笉褰卞搷鍒涘缓锛屽彧鎺у埗鍒濆鐘舵€侊級
918
+ // 根据配置的 visible 决定初始显隐(不影响创建,只控制初始状态)
901
919
  if (cat.visible === false) {
902
920
  this.setCategoryVisible(cat.key, false);
903
921
  }
904
922
  }
905
- console.log('[GaeaApp] 灞忓箷鍥炬爣鍒涘缓瀹屾垚, 鍏?', count);
906
- // 閫氱煡澶栭儴鍥炬爣宸插氨缁? document.dispatchEvent(new CustomEvent('gaea-icons-ready'));
923
+ console.log('[GaeaApp] 屏幕图标创建完成, 共:', count);
924
+ // 通知外部图标已就绪
925
+ document.dispatchEvent(new CustomEvent('gaea-icons-ready'));
907
926
  }
908
927
 
909
- // ---- 鍥惧眰鍒嗙被鏄鹃殣鎺у埗 ----
928
+ // ---- 图层分类显隐控制 ----
910
929
 
911
930
  /**
912
- * 璁剧疆鏌愪釜鍒嗙被鍥炬爣鐨勫彲瑙佹€? * @param {string} categoryKey 鍒嗙被 key锛堝 'seawall', 'hydrometricStations' 绛夛級
913
- * @param {boolean} visible 鏄惁鍙
931
+ * 设置某个分类图标的可见性
932
+ * @param {string} categoryKey 分类 key(如 'seawall', 'hydrometricStations' 等)
933
+ * @param {boolean} visible 是否可见
914
934
  */
915
935
  setCategoryVisible(categoryKey, visible) {
916
- // 璁板綍鍒嗙被鏄鹃殣鐘舵€? if (visible) {
936
+ // 记录分类显隐状态
937
+ if (visible) {
917
938
  this._hiddenCategories.delete(categoryKey)
918
939
  } else {
919
940
  this._hiddenCategories.add(categoryKey)
920
941
  }
921
942
  const els = document.querySelectorAll('.gaea-cat-' + categoryKey);
922
- console.log('[GaeaApp] setCategoryVisible:', categoryKey, visible, '鍏冪礌鏁?', els.length);
943
+ console.log('[GaeaApp] setCategoryVisible:', categoryKey, visible, '元素数:', els.length);
923
944
  els.forEach(el => { el.style.display = visible && this._markersVisible ? '' : 'none'; });
924
- // 鍚屾寮曟搸鍙鎬? const items = this._screenIconList.filter(item => item.category === categoryKey);
945
+ // 同步引擎可见性
946
+ const items = this._screenIconList.filter(item => item.category === categoryKey);
925
947
  items.forEach(item => {
926
948
  if (visible && this._markersVisible) {
927
949
  item.element.UseAutoVisible();
@@ -934,7 +956,8 @@ export class GaeaApp {
934
956
  }
935
957
 
936
958
  /**
937
- * 鑾峰彇鏌愪釜鍒嗙被鐨勫綋鍓嶅彲瑙佹€э紙鏍规嵁 DOM 鍏冪礌鍒ゆ柇锛? * @param {string} categoryKey
959
+ * 获取某个分类的当前可见性(根据 DOM 元素判断)
960
+ * @param {string} categoryKey
938
961
  * @returns {boolean}
939
962
  */
940
963
  getCategoryVisible(categoryKey) {
@@ -942,13 +965,13 @@ export class GaeaApp {
942
965
  return el ? el.style.display !== 'none' : true;
943
966
  }
944
967
 
945
- // ---- 鍏紑鏂规硶 ----
968
+ // ---- 公开方法 ----
946
969
 
947
- /** 鍒濆鍖栭粯璁ゅ浘灞傚拰鐩告満瑙嗚锛堟墜鍔ㄨ皟鐢紝鍙紶鍙傝鐩栭厤缃級 */
970
+ /** 初始化默认图层和相机视角(手动调用,可传参覆盖配置) */
948
971
  async initDefaultLayers({ layers, cameraPosition } = {}) {
949
972
  const cfg = this.config;
950
973
  await this.getAllServe();
951
- console.log('WMTS 鍥惧眰鏁伴噺:', this.layers.length);
974
+ console.log('WMTS 图层数量:', this.layers.length);
952
975
  this.layers.slice(0, 10).forEach(l => console.log(' -', l.Title));
953
976
 
954
977
  const pos = cameraPosition ?? cfg.defaultCameraPosition;
@@ -971,7 +994,7 @@ export class GaeaApp {
971
994
  const layers = Gaea.WMTSImageStore.ParserXml2Capabilities(hostUrl, xml);
972
995
  this.layers = [...this.layers, ...(layers || [])];
973
996
  } catch (e) {
974
- console.warn('getAllServe 澶辫触:', e.message);
997
+ console.warn('getAllServe 失败:', e.message);
975
998
  }
976
999
  return this.layers;
977
1000
  }
@@ -979,12 +1002,12 @@ export class GaeaApp {
979
1002
  addLayer({ layerName, max, min, transparency, forMat = 'image/vnd.jpeg-png', inserIndex = null } = {}) {
980
1003
  const mime = ['image/jpeg','image/png','image/tga','image/webp','image/bmp','application/json;type=geojson','image/vnd.jpeg-png'];
981
1004
  const mimeIdx = mime.indexOf(forMat);
982
- if (mimeIdx === -1) return console.warn('鏈煡鍥惧眰绫诲瀷:', forMat);
1005
+ if (mimeIdx === -1) return console.warn('未知图层类型:', forMat);
983
1006
 
984
- if (!this.layers?.length) return console.warn('鍥惧眰鍒楄〃涓虹┖锛岃鍏堣皟鐢?getAllServe()');
1007
+ if (!this.layers?.length) return console.warn('图层列表为空,请先调用 getAllServe()');
985
1008
 
986
1009
  const capability = this.layers.find(i => i.Title === layerName);
987
- if (!capability) return console.warn(`鍥惧眰 "${layerName}" 鏈壘鍒癭);
1010
+ if (!capability) return console.warn(`图层 "${layerName}" 未找到`);
988
1011
 
989
1012
  const store = new Gaea.WMTSImageStore();
990
1013
  store.ImageExtension = mimeIdx;
@@ -993,24 +1016,24 @@ export class GaeaApp {
993
1016
  if (max !== undefined) store.LevelRange = new Gaea.Vector2(min, max);
994
1017
  if (transparency !== undefined) store.ImageTransparency = transparency;
995
1018
 
996
- if (!store.CurrentCapabilitie.InstanceID) return console.error(`${layerName} 鍔犺浇寮傚父`);
1019
+ if (!store.CurrentCapabilitie.InstanceID) return console.error(`${layerName} 加载异常`);
997
1020
 
998
1021
  const tileset = Gaea.World.Instance.DefaultPyramidTileSet;
999
1022
  inserIndex !== null ? tileset.InsertImageStore(store, inserIndex) : tileset.AddImageStore(store);
1000
- console.log(`褰卞儚 "${layerName}" 娣诲姞鎴愬姛`);
1023
+ console.log(`影像 "${layerName}" 添加成功`);
1001
1024
  return store;
1002
1025
  }
1003
1026
 
1004
- // ---- 鍦板舰鏈嶅姟 ----
1027
+ // ---- 地形服务 ----
1005
1028
 
1006
- /** 鍔犺浇鍗曚釜鍦板舰鏈嶅姟 */
1029
+ /** 加载单个地形服务 */
1007
1030
  _initTerrainServer(url) {
1008
1031
  return new Promise((resolve) => {
1009
1032
  Gaea.World.Instance.InitTerrainServer2(url, () => resolve());
1010
1033
  });
1011
1034
  }
1012
1035
 
1013
- /** 鍔犺浇閰嶇疆涓殑鍦板舰鏈嶅姟锛坈fg.defaultTerrains锛寀rl 涓虹浉瀵?serveUrl 鐨勮矾寰勬垨瀹屾暣鍦板潃锛?*/
1036
+ /** 加载配置中的地形服务(cfg.defaultTerrains,url 为相对 serveUrl 的路径或完整地址) */
1014
1037
  async addInitTerrainServer(terrains) {
1015
1038
  const cfg = this.config;
1016
1039
  const list = terrains ?? cfg.defaultTerrains;
@@ -1019,20 +1042,20 @@ export class GaeaApp {
1019
1042
  const url = /^https?:\/\//.test(t.url) ? t.url : `${cfg.serveUrl}${t.url}`;
1020
1043
  try {
1021
1044
  await this._initTerrainServer(url);
1022
- console.log(`鍦板舰 "${t.name}" 鍔犺浇鎴愬姛`);
1045
+ console.log(`地形 "${t.name}" 加载成功`);
1023
1046
  } catch (e) {
1024
- console.warn(`鍦板舰 "${t.name}" 鍔犺浇澶辫触:`, e?.message);
1047
+ console.warn(`地形 "${t.name}" 加载失败:`, e?.message);
1025
1048
  }
1026
1049
  }
1027
1050
  }
1028
1051
 
1029
- // ---- 璐村湴绾匡紙鐭㈤噺绾挎湇鍔★級 ----
1052
+ // ---- 贴地线(矢量线服务) ----
1030
1053
 
1031
- /** 浠?WMTS 鏈嶅姟鍒楄〃鍒涘缓鐭㈤噺瑕佺礌鏈嶅姟 */
1054
+ /** 从 WMTS 服务列表创建矢量要素服务 */
1032
1055
  CreateFeature(serviceName, levelRange, render, alias) {
1033
- if (!this.layers?.length) return console.warn('鐭㈤噺鏈嶅姟鍒楄〃涓虹┖锛岃鍏堣皟鐢?getAllServe()');
1056
+ if (!this.layers?.length) return console.warn('矢量服务列表为空,请先调用 getAllServe()');
1034
1057
  const cap = this.layers.find(i => i.Title === serviceName);
1035
- if (!cap) return console.warn(`鐭㈤噺鏈嶅姟 "${serviceName}" 鏈壘鍒癭);
1058
+ if (!cap) return console.warn(`矢量服务 "${serviceName}" 未找到`);
1036
1059
  const store = new Gaea.WMTSVectorStore();
1037
1060
  store.Name = alias == null ? serviceName : alias;
1038
1061
  store.Init(cap);
@@ -1043,7 +1066,7 @@ export class GaeaApp {
1043
1066
  return store;
1044
1067
  }
1045
1068
 
1046
- /** 娣诲姞鍗曟潯璐村湴绾?*/
1069
+ /** 添加单条贴地线 */
1047
1070
  AddVTSLine({ name, range, color, index }) {
1048
1071
  const symbol = new Gaea.GroundPolylineSymbol();
1049
1072
  symbol.Width = 2;
@@ -1055,37 +1078,40 @@ export class GaeaApp {
1055
1078
  return render;
1056
1079
  }
1057
1080
 
1058
- /** 鍔犺浇閰嶇疆涓殑璐村湴绾匡紙cfg.defaultGroundLines锛屼緷璧?getAllServe 鐨勬湇鍔″垪琛級 */
1081
+ /** 加载配置中的贴地线(cfg.defaultGroundLines,依赖 getAllServe 的服务列表) */
1059
1082
  addGroundLines(groundLines) {
1060
1083
  const cfg = this.config;
1061
1084
  const list = groundLines ?? cfg.defaultGroundLines;
1062
1085
  if (!list?.length) return;
1063
- if (!this.layers?.length) return console.warn('璐村湴绾块渶瑕佸厛鍔犺浇 WMTS 鏈嶅姟鍒楄〃(getAllServe)');
1086
+ if (!this.layers?.length) return console.warn('贴地线需要先加载 WMTS 服务列表(getAllServe)');
1064
1087
  for (const item of list) {
1065
1088
  if (item.isInitLoad === false) continue;
1066
1089
  try {
1067
1090
  this.AddVTSLine(item);
1068
- console.log(`璐村湴绾?"${item.name}" 鍔犺浇鎴愬姛`);
1091
+ console.log(`贴地线 "${item.name}" 加载成功`);
1069
1092
  } catch (e) {
1070
- console.warn(`璐村湴绾?"${item.name}" 鍔犺浇澶辫触:`, e?.message);
1093
+ console.warn(`贴地线 "${item.name}" 加载失败:`, e?.message);
1071
1094
  }
1072
1095
  }
1073
1096
  }
1074
1097
 
1075
- // ---- 璐村湴闈紙鐭㈤噺闈㈢粯鍒讹級 ----
1098
+ // ---- 贴地面(矢量面绘制) ----
1076
1099
 
1077
1100
  /**
1078
- * 鏍规嵁鍧愭爣鏁扮粍娣诲姞璐村湴闈? * @param {Array} coords - 鍧愭爣鏁扮粍锛屾牸寮?[[lng, lat], [lng, lat], ...]
1079
- * @param {string} name - 闈㈠悕绉? * @param {string} colorHex - 棰滆壊 hex 鍊硷紝濡?'#3498db'
1080
- * @returns {object|null} 鍒涘缓鐨?PolygonGeometryElement锛屽け璐ヨ繑鍥?null
1101
+ * 根据坐标数组添加贴地面
1102
+ * @param {Array} coords - 坐标数组,格式 [[lng, lat], [lng, lat], ...]
1103
+ * @param {string} name - 面名称
1104
+ * @param {string} colorHex - 颜色 hex 值,如 '#3498db'
1105
+ * @returns {object|null} 创建的 PolygonGeometryElement,失败返回 null
1081
1106
  */
1082
1107
  addGroundPolygonByCoords(coords, name, colorHex) {
1083
1108
  if (!coords || coords.length < 3) {
1084
- console.warn(`鍧愭爣鐐逛笉瓒筹紝璺宠繃: ${name}`);
1109
+ console.warn(`坐标点不足,跳过: ${name}`);
1085
1110
  return null;
1086
1111
  }
1087
- // 妫€鏌ユ槸鍚﹀凡瀛樺湪鍚屽悕闈? if (this._groundPolygonElements.find(p => p.name === name)) {
1088
- console.warn(`璐村湴闈?"${name}" 宸插瓨鍦紝璺宠繃`);
1112
+ // 检查是否已存在同名面
1113
+ if (this._groundPolygonElements.find(p => p.name === name)) {
1114
+ console.warn(`贴地面 "${name}" 已存在,跳过`);
1089
1115
  return null;
1090
1116
  }
1091
1117
 
@@ -1121,36 +1147,36 @@ export class GaeaApp {
1121
1147
  return polygonElement;
1122
1148
  }
1123
1149
 
1124
- /** 绉婚櫎鎸囧畾璐村湴闈?*/
1150
+ /** 移除指定贴地面 */
1125
1151
  removeGroundPolygon(element) {
1126
1152
  try {
1127
1153
  Gaea.World.Instance.RenderableObjectList.Remove(element);
1128
1154
  } catch (err) {
1129
- console.error('绉婚櫎璐村湴闈㈠け璐?', err);
1155
+ console.error('移除贴地面失败:', err);
1130
1156
  }
1131
1157
  const idx = this._groundPolygonElements.findIndex(p => p.element === element);
1132
1158
  if (idx >= 0) this._groundPolygonElements.splice(idx, 1);
1133
1159
  }
1134
1160
 
1135
- /** 娓呴櫎鎵€鏈夎创鍦伴潰 */
1161
+ /** 清除所有贴地面 */
1136
1162
  clearAllGroundPolygons() {
1137
1163
  this._groundPolygonElements.forEach(item => {
1138
1164
  try {
1139
1165
  Gaea.World.Instance.RenderableObjectList.Remove(item.element);
1140
1166
  } catch (err) {
1141
- console.error('绉婚櫎璐村湴闈㈠け璐?', err);
1167
+ console.error('移除贴地面失败:', err);
1142
1168
  }
1143
1169
  });
1144
1170
  this._groundPolygonElements = [];
1145
1171
  }
1146
1172
 
1147
- /** 瀹氫綅鍒版寚瀹氳瑙掞紙缁忕含搴﹂珮绋?+ 鐩告満鍙傛暟锛?*/
1173
+ /** 定位到指定视角(经纬度高程 + 相机参数) */
1148
1174
  SetPosition(numStr) {
1149
1175
  const arr = numStr.split(',').map(s => Number(s.trim()));
1150
1176
  Gaea.World.Instance.DefaultCamera.SetPosition2(arr[0], arr[1], arr[2], arr[3], arr[4], arr[5]);
1151
1177
  }
1152
1178
 
1153
- /** 椋炲埌鍥惧眰鏍戣妭鐐逛綅缃?*/
1179
+ /** 飞到图层树节点位置 */
1154
1180
  flyToNode(node) {
1155
1181
  if (!node?.position) return;
1156
1182
  const { x, y, z } = node.position;
@@ -1159,8 +1185,9 @@ export class GaeaApp {
1159
1185
  }
1160
1186
 
1161
1187
  /**
1162
- * 鏍规嵁鍚嶇О鎴?id 瀹氫綅鍒版捣濉? * @param {string} name - 娴峰鍚嶇О鎴?id
1163
- * @param {string} [tilesetUrl] - 鍙€夛紝鍊炬枩鎽勫奖 3D Tiles 鐨?tileset.json 鍦板潃
1188
+ * 根据名称或 id 定位到海塘
1189
+ * @param {string} name - 海塘名称或 id
1190
+ * @param {string} [tilesetUrl] - 可选,倾斜摄影 3D Tiles 的 tileset.json 地址
1164
1191
  */
1165
1192
  flyToSeawall = (name, tilesetUrl) => {
1166
1193
  const cats = this.layerCategories || []
@@ -1175,12 +1202,12 @@ export class GaeaApp {
1175
1202
  return
1176
1203
  }
1177
1204
  }
1178
- console.warn(`[GaeaApp] 鏈壘鍒版捣濉? ${name}`)
1205
+ console.warn(`[GaeaApp] 未找到海塘: ${name}`)
1179
1206
  };
1180
1207
 
1181
1208
  /**
1182
- * 鍔犺浇鍊炬枩鎽勫奖 3D Tiles
1183
- * @param {Object} item - { jsonUrl: tileset.json鍦板潃, tilName?: 鍚嶇О, height?: 楂樺害鍋忕Щ, isInitLoad?: 鏄惁鍒濆鍖栧姞杞?}
1209
+ * 加载倾斜摄影 3D Tiles
1210
+ * @param {Object} item - { jsonUrl: tileset.json地址, tilName?: 名称, height?: 高度偏移, isInitLoad?: 是否初始化加载 }
1184
1211
  */
1185
1212
  add3DTiles = (item) => {
1186
1213
  const tilesetUrl = typeof item === 'string' ? item : item.jsonUrl
@@ -1207,43 +1234,45 @@ export class GaeaApp {
1207
1234
  if (name) {
1208
1235
  this._tilesetMap[name] = tileset
1209
1236
  }
1210
- console.log('[GaeaApp] 3D Tiles 宸插姞杞?', name || tilesetUrl, height ? `楂樺害: ${height}` : '', enabledClip ? '(瑁佸壀宸插惎鐢?' : '')
1237
+ console.log('[GaeaApp] 3D Tiles 已加载:', name || tilesetUrl, height ? `高度: ${height}` : '', enabledClip ? '(裁剪已启用)' : '')
1211
1238
  return tileset
1212
1239
  };
1213
1240
 
1214
- // ---- 鍊炬枩鎸栨柟 ----
1241
+ // ---- 倾斜挖方 ----
1215
1242
 
1216
1243
  /**
1217
- * 鏍规嵁澶氳竟褰㈠潗鏍囪鍓湴褰紙鍊炬枩鎸栨柟锛? * @param {Array<{x: number, y: number, z: number}>} pointList - 澶氳竟褰㈤《鐐瑰潗鏍囨暟缁?[{x: 绾害, y: 缁忓害, z: 楂樼▼}, ...]
1218
- * @param {number} height - 瑁佸壀娣卞害/楂樺害
1219
- * @param {Function} [callback] - 瀹屾垚鍚庣殑鍥炶皟鍑芥暟锛屾帴鏀?_Holeitem
1220
- * @returns {boolean} 鏄惁鎴愬姛
1244
+ * 根据多边形坐标裁剪地形(倾斜挖方)
1245
+ * @param {Array<{x: number, y: number, z: number}>} pointList - 多边形顶点坐标数组 [{x: 纬度, y: 经度, z: 高程}, ...]
1246
+ * @param {number} height - 裁剪深度/高度
1247
+ * @param {Function} [callback] - 完成后的回调函数,接收 _Holeitem
1248
+ * @returns {boolean} 是否成功
1221
1249
  * @example
1222
1250
  * app.editTerrainTool([
1223
1251
  * { x: 27.2205, y: 120.456, z: 10 },
1224
1252
  * { x: 27.2210, y: 120.458, z: 10 },
1225
1253
  * { x: 27.2195, y: 120.458, z: 10 }
1226
- * ], 50, (item) => { console.log('鎸栨柟瀹屾垚', item) })
1254
+ * ], 50, (item) => { console.log('挖方完成', item) })
1227
1255
  */
1228
1256
  editTerrainTool = (pointList, height, callback) => {
1229
1257
  if (!pointList || pointList.length === 0) {
1230
- console.warn('[GaeaApp] editTerrainTool: pointList 涓嶈兘涓虹┖')
1258
+ console.warn('[GaeaApp] editTerrainTool: pointList 不能为空')
1231
1259
  return false
1232
1260
  }
1233
1261
 
1234
1262
  const Position = pointList.map(point => new Gaea.Vector3(point.x, point.y, point.z))
1235
1263
 
1236
- // 缁忕含搴?鈫?绗涘崱灏斿潗鏍? const originPoint = pointList.map(p => Gaea.GaeaMath.SphericalToCartesianDeg(new Gaea.Vector3(p.x, p.y, p.z)))
1264
+ // 经纬度 → 笛卡尔坐标
1265
+ const originPoint = pointList.map(p => Gaea.GaeaMath.SphericalToCartesianDeg(new Gaea.Vector3(p.x, p.y, p.z)))
1237
1266
 
1238
- // 鍚敤 QueryServer
1267
+ // 启用 QueryServer
1239
1268
  Gaea.QueryServer.Instance.Enable = true
1240
1269
 
1241
- // 娓呮棫鎸栨柟
1270
+ // 清旧挖方
1242
1271
  if (this._holeItem) {
1243
1272
  Gaea.ExcavationManager.Instance.RemoveItem(this._holeItem)
1244
1273
  }
1245
1274
 
1246
- // 鍒涘缓鎸栨柟瀵硅薄 (鍧愭爣鐐? 鎻掑€艰窛绂? 娣卞害, UV缂╂斁, 鏄惁娉曠嚎鏉愯川, 鏄惁闇€瑕乪dge)
1275
+ // 创建挖方对象 (坐标点, 插值距离, 深度, UV缩放, 是否法线材质, 是否需要edge)
1247
1276
  this._holeItem = Gaea.ExcavationManager.Instance.CreateItem(originPoint, 100, height, 100, true, true)
1248
1277
  Gaea.ExcavationManager.Instance.ExposureLength = 0
1249
1278
  Gaea.ExcavationManager.Instance.AddItem(this._holeItem)
@@ -1256,7 +1285,7 @@ export class GaeaApp {
1256
1285
  Gaea.QueryServer.Instance.ClipBox = box
1257
1286
  }
1258
1287
 
1259
- console.log('[GaeaApp] 鎸栨柟宸叉墽琛?', `娣卞害: ${height}`, `椤剁偣鏁? ${pointList.length}`)
1288
+ console.log('[GaeaApp] 挖方已执行:', `深度: ${height}`, `顶点数: ${pointList.length}`)
1260
1289
 
1261
1290
  setTimeout(() => {
1262
1291
  if (typeof callback === 'function') {
@@ -1266,7 +1295,7 @@ export class GaeaApp {
1266
1295
  };
1267
1296
 
1268
1297
  /**
1269
- * 杩樺師鍦板舰锛堟竻闄ゆ墍鏈夋寲鏂规晥鏋滐級
1298
+ * 还原地形(清除所有挖方效果)
1270
1299
  */
1271
1300
  restoreTerrain = () => {
1272
1301
  if (this._holeItem) {
@@ -1279,26 +1308,27 @@ export class GaeaApp {
1279
1308
  Gaea.QueryServer.Instance.ClipBox.QueueFree()
1280
1309
  Gaea.QueryServer.Instance.ClipBox = null
1281
1310
  }
1282
- console.log('[GaeaApp] 鍦板舰宸茶繕鍘?)
1311
+ console.log('[GaeaApp] 地形已还原')
1283
1312
  };
1284
1313
 
1285
1314
  /**
1286
- * 鐩戝惉鐢诲竷榧犳爣鐐瑰嚮锛岃幏鍙栫偣鍑讳綅缃殑缁忕含搴﹀潗鏍? * @example
1315
+ * 监听画布鼠标点击,获取点击位置的经纬度坐标
1316
+ * @example
1287
1317
  * app.addMouseClick((pos) => {
1288
- * console.log('鐐瑰嚮浣嶇疆:', pos) // { x: 绾害, y: 缁忓害, z: 楂樼▼ }
1318
+ * console.log('点击位置:', pos) // { x: 纬度, y: 经度, z: 高程 }
1289
1319
  * })
1290
1320
  */
1291
1321
  addMouseClick = (callback) => {
1292
1322
  const canvas = document.getElementById('canvas')
1293
1323
  if (!canvas) {
1294
- console.warn('[GaeaApp] addMouseClick: canvas 鍏冪礌鏈壘鍒?)
1324
+ console.warn('[GaeaApp] addMouseClick: canvas 元素未找到')
1295
1325
  return
1296
1326
  }
1297
1327
  canvas.addEventListener('mousedown', (event) => {
1298
1328
  const result = Gaea.GaeaMath.ProjectPosition(new Gaea.Vector2(event.offsetX, event.offsetY))
1299
1329
  const lonlat = Gaea.GaeaMath.CartesianToSphericalDeg(result)
1300
1330
  const pos = { x: lonlat.x, y: lonlat.y, z: lonlat.z }
1301
- console.log('[GaeaApp] 鐐瑰嚮浣嶇疆:', pos)
1331
+ console.log('[GaeaApp] 点击位置:', pos)
1302
1332
  if (typeof callback === 'function') {
1303
1333
  callback(pos)
1304
1334
  }
@@ -1306,17 +1336,17 @@ export class GaeaApp {
1306
1336
  };
1307
1337
 
1308
1338
  /**
1309
- * 鑾峰彇宸插姞杞界殑妯″瀷鍏冪礌
1310
- * @param {string} name - 妯″瀷鍚嶇О
1311
- * @returns {object} GLTFElement 鎴?null
1339
+ * 获取已加载的模型元素
1340
+ * @param {string} name - 模型名称
1341
+ * @returns {object} GLTFElement 或 null
1312
1342
  */
1313
1343
  getModel = (name) => {
1314
1344
  return this._gltfElements[name] || null
1315
1345
  };
1316
1346
 
1317
1347
  /**
1318
- * 鑾峰彇妯″瀷褰撳墠鐘舵€侊紙浣嶇疆銆佹棆杞€佺缉鏀撅級
1319
- * @param {string} name - 妯″瀷鍚嶇О
1348
+ * 获取模型当前状态(位置、旋转、缩放)
1349
+ * @param {string} name - 模型名称
1320
1350
  * @returns {object|null} { position, rotate, scale, visible }
1321
1351
  */
1322
1352
  getModelInfo = (name) => {
@@ -1331,68 +1361,72 @@ export class GaeaApp {
1331
1361
  };
1332
1362
 
1333
1363
  /**
1334
- * 璁剧疆妯″瀷浣嶇疆锛堢粡绾害 + 楂樼▼锛? * @param {string} name - 妯″瀷鍚嶇О
1335
- * @param {object} position - { x: 绾害, y: 缁忓害, z: 楂樼▼ }
1364
+ * 设置模型位置(经纬度 + 高程)
1365
+ * @param {string} name - 模型名称
1366
+ * @param {object} position - { x: 纬度, y: 经度, z: 高程 }
1336
1367
  */
1337
1368
  setModelPosition = (name, position) => {
1338
1369
  const el = this._gltfElements[name]
1339
- if (!el) { console.warn(`[GaeaApp] 妯″瀷 "${name}" 鏈壘鍒癭); return }
1370
+ if (!el) { console.warn(`[GaeaApp] 模型 "${name}" 未找到`); return }
1340
1371
  el.GeographyPosition = new Gaea.Vector3(position.x, position.y, position.z)
1341
1372
  };
1342
1373
 
1343
1374
  /**
1344
- * 璁剧疆妯″瀷鏃嬭浆
1345
- * @param {string} name - 妯″瀷鍚嶇О
1375
+ * 设置模型旋转
1376
+ * @param {string} name - 模型名称
1346
1377
  * @param {number[]} rotate - [pitch, yaw, roll]
1347
1378
  */
1348
1379
  setModelRotation = (name, rotate) => {
1349
1380
  const el = this._gltfElements[name]
1350
- if (!el) { console.warn(`[GaeaApp] 妯″瀷 "${name}" 鏈壘鍒癭); return }
1381
+ if (!el) { console.warn(`[GaeaApp] 模型 "${name}" 未找到`); return }
1351
1382
  el.TiltHeadingBank = new Gaea.Vector3(rotate[0], rotate[1], rotate[2])
1352
1383
  };
1353
1384
 
1354
1385
  /**
1355
- * 璁剧疆妯″瀷缂╂斁
1356
- * @param {string} name - 妯″瀷鍚嶇О
1386
+ * 设置模型缩放
1387
+ * @param {string} name - 模型名称
1357
1388
  * @param {number[]} size - [sx, sy, sz]
1358
1389
  */
1359
1390
  setModelScale = (name, size) => {
1360
1391
  const el = this._gltfElements[name]
1361
- if (!el) { console.warn(`[GaeaApp] 妯″瀷 "${name}" 鏈壘鍒癭); return }
1392
+ if (!el) { console.warn(`[GaeaApp] 模型 "${name}" 未找到`); return }
1362
1393
  el.Root.Scale = new Gaea.Vector3(size[0], size[1], size[2])
1363
1394
  };
1364
1395
 
1365
1396
  /**
1366
- * 璁剧疆妯″瀷鍙鎬? * @param {string} name - 妯″瀷鍚嶇О
1397
+ * 设置模型可见性
1398
+ * @param {string} name - 模型名称
1367
1399
  * @param {boolean} visible
1368
1400
  */
1369
1401
  setModelVisible = (name, visible) => {
1370
1402
  const el = this._gltfElements[name]
1371
- if (!el) { console.warn(`[GaeaApp] 妯″瀷 "${name}" 鏈壘鍒癭); return }
1403
+ if (!el) { console.warn(`[GaeaApp] 模型 "${name}" 未找到`); return }
1372
1404
  el.Root.Visible = visible
1373
1405
  };
1374
1406
 
1375
1407
  /**
1376
- * 绉婚櫎妯″瀷锛堝弬鑰?ht_3dview removeGltf锛? * @param {string} name - 妯″瀷鍚嶇О
1408
+ * 移除模型(参考 ht_3dview removeGltf)
1409
+ * @param {string} name - 模型名称
1377
1410
  */
1378
1411
  removeGltf = (name) => {
1379
1412
  const el = this._gltfElements[name]
1380
- if (!el) { console.warn(`[GaeaApp] 妯″瀷 "${name}" 鏈壘鍒癭); return }
1413
+ if (!el) { console.warn(`[GaeaApp] 模型 "${name}" 未找到`); return }
1381
1414
  Gaea.World.Instance.RenderableObjectList.Remove(el)
1382
1415
  delete this._gltfElements[name]
1383
1416
  delete this._gltfCfgMap[name]
1384
1417
  };
1385
1418
 
1386
1419
  /**
1387
- * 淇敼妯″瀷閰嶇疆锛堜綅缃?鏃嬭浆/缂╂斁涓€璧疯缃紝鍙傝€?ht_3dview updateElementPosition锛? * @param {object} options
1388
- * @param {string} options.name - 妯″瀷鍚嶇О
1389
- * @param {object} options.position - { x: 绾害, y: 缁忓害, z: 楂樼▼ }
1420
+ * 修改模型配置(位置/旋转/缩放一起设置,参考 ht_3dview updateElementPosition)
1421
+ * @param {object} options
1422
+ * @param {string} options.name - 模型名称
1423
+ * @param {object} options.position - { x: 纬度, y: 经度, z: 高程 }
1390
1424
  * @param {number[]} [options.rotate] - [pitch, yaw, roll]
1391
1425
  * @param {number[]} [options.size] - [sx, sy, sz]
1392
1426
  */
1393
1427
  updateElementPosition = ({ name, position, rotate, size }) => {
1394
1428
  const el = this._gltfElements[name]
1395
- if (!el) { console.warn(`[GaeaApp] 妯″瀷 "${name}" 鏈壘鍒癭); return }
1429
+ if (!el) { console.warn(`[GaeaApp] 模型 "${name}" 未找到`); return }
1396
1430
  if (position) {
1397
1431
  el.GeographyPosition = new Gaea.Vector3(position.x, position.y, position.z)
1398
1432
  }
@@ -1405,8 +1439,11 @@ export class GaeaApp {
1405
1439
  };
1406
1440
 
1407
1441
  /**
1408
- * 娣诲姞榧犳爣绉诲姩鐩戝惉锛堝弬鑰?ht_3dview addMouseMove锛屼娇鐢?Gaea 寮曟搸浜嬩欢锛? * 鍥炶皟杩斿洖褰撳墠榧犳爣鎵€鍦ㄤ綅缃殑缁忕含搴﹀潗鏍? * @param {function} cb - (pos: {x,y,z}) => void
1409
- * @returns {function} 璋冪敤鍙彇娑堢洃鍚? */
1442
+ * 添加鼠标移动监听(参考 ht_3dview addMouseMove,使用 Gaea 引擎事件)
1443
+ * 回调返回当前鼠标所在位置的经纬度坐标
1444
+ * @param {function} cb - (pos: {x,y,z}) => void
1445
+ * @returns {function} 调用可取消监听
1446
+ */
1410
1447
  addMouseMove = (cb = null) => {
1411
1448
  const handle = Gaea.World.Instance.OnMouseMove((p, b) => {
1412
1449
  const screenpos = new Gaea.Vector2(p.x, p.y)
@@ -1423,7 +1460,7 @@ export class GaeaApp {
1423
1460
  };
1424
1461
 
1425
1462
  /**
1426
- * 绉婚櫎榧犳爣绉诲姩鐩戝惉
1463
+ * 移除鼠标移动监听
1427
1464
  */
1428
1465
  removeMouseMove = () => {
1429
1466
  if (this._mouseMoveHandle) {
@@ -1433,12 +1470,15 @@ export class GaeaApp {
1433
1470
  };
1434
1471
 
1435
1472
  /**
1436
- * 娣诲姞榧犳爣鎶捣鐩戝惉锛堝弬鑰?ht_3dview锛屼娇鐢?Gaea 寮曟搸 OnMouseUp 浜嬩欢锛? * 鍥炶皟杩斿洖鐐瑰嚮浣嶇疆鐨勭粡绾害鍧愭爣
1473
+ * 添加鼠标抬起监听(参考 ht_3dview,使用 Gaea 引擎 OnMouseUp 事件)
1474
+ * 回调返回点击位置的经纬度坐标
1437
1475
  * @param {function} cb - (pos: {x,y,z}) => void
1438
- * @returns {function} 璋冪敤鍙彇娑堢洃鍚? */
1476
+ * @returns {function} 调用可取消监听
1477
+ */
1439
1478
  addMouseUp = (cb = null) => {
1440
1479
  const handle = Gaea.World.Instance.OnMouseUp((p, b) => {
1441
- if (b !== 1) return // 浠呭乏閿? const screenpos = new Gaea.Vector2(p.x, p.y)
1480
+ if (b !== 1) return // 仅左键
1481
+ const screenpos = new Gaea.Vector2(p.x, p.y)
1442
1482
  const spacepos = Gaea.GaeaMath.ProjectPosition(screenpos)
1443
1483
  const latlonalt = Gaea.GaeaMath.CartesianToSphericalDeg(spacepos)
1444
1484
  const pos = { x: latlonalt.x, y: latlonalt.y, z: latlonalt.z }
@@ -1452,7 +1492,7 @@ export class GaeaApp {
1452
1492
  };
1453
1493
 
1454
1494
  /**
1455
- * 绉婚櫎榧犳爣鎶捣鐩戝惉
1495
+ * 移除鼠标抬起监听
1456
1496
  */
1457
1497
  removeMouseUp = () => {
1458
1498
  if (this._mouseUpHandle) {
@@ -1462,9 +1502,11 @@ export class GaeaApp {
1462
1502
  };
1463
1503
 
1464
1504
  /**
1465
- * 浜や簰寮忔嬀鍙栧潗鏍囷紙鐐瑰嚮鍦板浘鑾峰彇缁忕含搴︼紝鍙傝€?ht_3dview 閫夋嫨鍧愭爣娴佺▼锛? * 鐐瑰嚮鍦板浘鍚庡洖璋冭繑鍥炲潗鏍囷紝鑷姩鍙栨秷鐩戝惉
1505
+ * 交互式拾取坐标(点击地图获取经纬度,参考 ht_3dview 选择坐标流程)
1506
+ * 点击地图后回调返回坐标,自动取消监听
1466
1507
  * @param {function} cb - (pos: {x,y,z}) => void
1467
- * @returns {function} 璋冪敤鍙彇娑堟嬀鍙? */
1508
+ * @returns {function} 调用可取消拾取
1509
+ */
1468
1510
  pickPosition = (cb) => {
1469
1511
  const cancel = this.addMouseUp((pos) => {
1470
1512
  cancel()
@@ -1474,9 +1516,10 @@ export class GaeaApp {
1474
1516
  };
1475
1517
 
1476
1518
  /**
1477
- * 浜や簰寮忔嬀鍙栨ā鍨嬶紝杩斿洖妯″瀷淇℃伅
1519
+ * 交互式拾取模型,返回模型信息
1478
1520
  * @param {function} callback - (modelInfo) => void
1479
- * @returns {function} 璋冪敤鍙彇娑堟嬀鍙? */
1521
+ * @returns {function} 调用可取消拾取
1522
+ */
1480
1523
  pickModel = (callback) => {
1481
1524
  const canvas = document.getElementById('canvas')
1482
1525
  if (!canvas) return () => {}
@@ -1498,7 +1541,8 @@ export class GaeaApp {
1498
1541
  };
1499
1542
 
1500
1543
  /**
1501
- * 鑾峰彇鎵€鏈夊彲閫夋ā鍨嬪垪琛紙鐢ㄤ簬闈㈡澘涓嬫媺閫夋嫨锛屽弬鑰?ht_3dview gaeaData.gltfList锛? * @returns {Array<{name: string, url: string, position?: object}>}
1544
+ * 获取所有可选模型列表(用于面板下拉选择,参考 ht_3dview gaeaData.gltfList)
1545
+ * @returns {Array<{name: string, url: string, position?: object}>}
1502
1546
  */
1503
1547
  getGltfList = () => {
1504
1548
  return Object.values(this._gltfCfgMap).map(cfg => ({
@@ -1508,10 +1552,12 @@ export class GaeaApp {
1508
1552
  };
1509
1553
 
1510
1554
  /**
1511
- * 鍔犺浇鐢ㄦ埛鑷畾涔夋ā鍨嬶紙鍙傝€?ht_3dview addGltf锛? * 鏂板妯″瀷鍒板満鏅苟璁板綍鍒伴厤缃〃
1555
+ * 加载用户自定义模型(参考 ht_3dview addGltf)
1556
+ * 新增模型到场景并记录到配置表
1512
1557
  * @param {object} cfg
1513
- * @param {string} cfg.name - 妯″瀷瀹炰緥鍚嶏紙涓嶈兘閲嶅锛? * @param {string} cfg.url - 妯″瀷 URL
1514
- * @param {object} cfg.position - { x: 绾害, y: 缁忓害, z: 楂樼▼ }
1558
+ * @param {string} cfg.name - 模型实例名(不能重复)
1559
+ * @param {string} cfg.url - 模型 URL
1560
+ * @param {object} cfg.position - { x: 纬度, y: 经度, z: 高程 }
1515
1561
  * @param {number[]} cfg.rotate - [pitch, yaw, roll]
1516
1562
  * @param {number[]} cfg.size - [sx, sy, sz]
1517
1563
  * @param {boolean} [cfg.show=true]
@@ -1521,10 +1567,10 @@ export class GaeaApp {
1521
1567
  addGltf = async (cfg) => {
1522
1568
  const { name, url, position, rotate, size, show = true, enablePick = true } = cfg;
1523
1569
  if (!name || !url || !position || !rotate || !size) {
1524
- console.warn('[GaeaApp] addGltf: 缂哄皯蹇呰鍙傛暟', cfg);
1570
+ console.warn('[GaeaApp] addGltf: 缺少必要参数', cfg);
1525
1571
  return null;
1526
1572
  }
1527
- // 璁板綍鍒伴厤缃〃锛屼緵鍚庣画闈㈡澘缂栬緫
1573
+ // 记录到配置表,供后续面板编辑
1528
1574
  this._gltfCfgMap[name] = {
1529
1575
  name, url, position: { ...position }, rotate: [...rotate], size: [...size],
1530
1576
  show, enablePick, isUserAdd: true
@@ -1537,21 +1583,25 @@ export class GaeaApp {
1537
1583
  };
1538
1584
 
1539
1585
  /**
1540
- * 鏍规嵁缁忕含搴﹂鍒版寚瀹氫綅缃? * @param {number} lat - 绾害
1541
- * @param {number} lng - 缁忓害
1542
- * @param {number} [z=0] - 鍦伴潰楂樼▼
1543
- * @param {number} [alt=500] - 鐩告満楂樺害
1544
- * @param {number} [pitch=0] - 淇话瑙掞紝榛樿 0锛堝瀭鐩翠刊瑙嗭級锛岃礋鍊煎悜鍓嶅€炬枩
1545
- * @param {number} [yaw=0] - 鍋忚埅瑙? */
1586
+ * 根据经纬度飞到指定位置
1587
+ * @param {number} lat - 纬度
1588
+ * @param {number} lng - 经度
1589
+ * @param {number} [z=0] - 地面高程
1590
+ * @param {number} [alt=500] - 相机高度
1591
+ * @param {number} [pitch=0] - 俯仰角,默认 0(垂直俯视),负值向前倾斜
1592
+ * @param {number} [yaw=0] - 偏航角
1593
+ */
1546
1594
  flyToPosition = (lat, lng, z = 0, alt = 500, pitch = 0, yaw = 0) => {
1547
1595
  this.SetPosition(`${lat},${lng},${z},${alt},${pitch},${yaw}`)
1548
1596
  };
1549
1597
 
1550
- // ---- 娌跨嚎椋炶 ----
1598
+ // ---- 沿线飞行 ----
1551
1599
 
1552
1600
  /**
1553
- * 娌跨嚎椋炶 - 鐩告満娌胯矾寰勮嚜鍔ㄩ琛? * @param {Array} lineData - 璺緞鐐规暟缁勶紝姣忎釜鐐?{x: 绾害, y: 缁忓害, z?: 楂樺害}
1554
- * @param {Object} options - 椋炶閰嶇疆锛坔eight/step/duration/xrotation/lockEye/minZoom/maxZoom锛? * @returns {Promise} 椋炶瀹屾垚鍚?resolve
1601
+ * 沿线飞行 - 相机沿路径自动飞行
1602
+ * @param {Array} lineData - 路径点数组,每个点 {x: 纬度, y: 经度, z?: 高度}
1603
+ * @param {Object} options - 飞行配置(height/step/duration/xrotation/lockEye/minZoom/maxZoom)
1604
+ * @returns {Promise} 飞行完成后 resolve
1555
1605
  */
1556
1606
  flyAlongPath = async (lineData, options = {}) => {
1557
1607
  const opts = {
@@ -1566,8 +1616,8 @@ export class GaeaApp {
1566
1616
  };
1567
1617
 
1568
1618
  if (!lineData || lineData.length < 2) {
1569
- console.error('flyAlongPath: 鑷冲皯闇€瑕?涓矾寰勭偣');
1570
- return Promise.reject(new Error('鑷冲皯闇€瑕?涓矾寰勭偣'));
1619
+ console.error('flyAlongPath: 至少需要2个路径点');
1620
+ return Promise.reject(new Error('至少需要2个路径点'));
1571
1621
  }
1572
1622
 
1573
1623
  try {
@@ -1616,28 +1666,29 @@ export class GaeaApp {
1616
1666
 
1617
1667
  return new Promise((resolve) => {
1618
1668
  this._flyAnimationPlayer.PlayWithCallback('flyPathAnimation', Gaea.CallbackType.AnimatioFinished, () => {
1619
- console.log('娌跨嚎椋炶鍔ㄧ敾鎾斁瀹屾垚');
1669
+ console.log('沿线飞行动画播放完成');
1620
1670
  resolve();
1621
1671
  });
1622
1672
  });
1623
1673
  } catch (error) {
1624
- console.error('flyAlongPath 鍚姩澶辫触:', error);
1674
+ console.error('flyAlongPath 启动失败:', error);
1625
1675
  this._cleanupFlyResources();
1626
1676
  return Promise.reject(error);
1627
1677
  }
1628
1678
  };
1629
1679
 
1630
- /** 鍋滄娌跨嚎椋炶 */
1680
+ /** 停止沿线飞行 */
1631
1681
  stopFlyAlongPath = () => {
1632
1682
  try {
1633
- // Godot AnimationPlayer.Stop(reset:bool)锛屽弬鏁版槸甯冨皵鑰岄潪鍔ㄧ敾鍚? if (this._flyAnimationPlayer) this._flyAnimationPlayer.Stop(false);
1683
+ // Godot AnimationPlayer.Stop(reset:bool),参数是布尔而非动画名
1684
+ if (this._flyAnimationPlayer) this._flyAnimationPlayer.Stop(false);
1634
1685
  } catch (error) {
1635
- console.error('鍋滄椋炶澶辫触:', error);
1686
+ console.error('停止飞行失败:', error);
1636
1687
  }
1637
1688
  this._cleanupFlyResources();
1638
1689
  };
1639
1690
 
1640
- /** 娓呯悊椋炶鐩稿叧璧勬簮 */
1691
+ /** 清理飞行相关资源 */
1641
1692
  _cleanupFlyResources = () => {
1642
1693
  try {
1643
1694
  if (this._flyAnimationPlayer) {
@@ -1652,11 +1703,11 @@ export class GaeaApp {
1652
1703
  Gaea.World.Instance.CameraController.LockedEntity = null;
1653
1704
  this._flyAnimation = null;
1654
1705
  } catch (error) {
1655
- console.error('娓呯悊椋炶璧勬簮澶辫触:', error);
1706
+ console.error('清理飞行资源失败:', error);
1656
1707
  }
1657
1708
  };
1658
1709
 
1659
- /** 鐩告満澶嶄綅鍒伴粯璁よ瑙?*/
1710
+ /** 相机复位到默认视角 */
1660
1711
  cameraReset = () => {
1661
1712
  this._cleanupFlyResources();
1662
1713
  const pos = this.config.defaultCameraPosition;
@@ -1664,13 +1715,14 @@ export class GaeaApp {
1664
1715
  };
1665
1716
 
1666
1717
  /**
1667
- * 娣诲姞灞忓箷鍥炬爣锛堝弬鑰?ht_3dview 涓夌鍒涘缓鏂瑰紡锛? * @param {string} opts.method - 'textImage' | 'styled' | 'imageOnly'
1718
+ * 添加屏幕图标(参考 ht_3dview 三种创建方式)
1719
+ * @param {string} opts.method - 'textImage' | 'styled' | 'imageOnly'
1668
1720
  */
1669
1721
  addScreenIcon({ name, text, imgUrl, position, method = 'textImage', offset = [0, 0], visibleRange = [10, 50000], anchorType = 6, drawLine = false, onClick, type = null, category = null, divId: externalDivId, elementName, elementChildrenIndex } = {}) {
1670
1722
  const safeId = name.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, '_');
1671
1723
  const divId = externalDivId || 'gaea-icon-' + safeId;
1672
1724
 
1673
- // 濡傛灉鏄閮ㄤ紶鍏ョ殑 divId锛岀洿鎺ヤ娇鐢ㄥ凡鏈夌殑 DOM 鍏冪礌
1725
+ // 如果是外部传入的 divId,直接使用已有的 DOM 元素
1674
1726
  let container;
1675
1727
  if (externalDivId) {
1676
1728
  container = document.getElementById(externalDivId);
@@ -1720,7 +1772,8 @@ export class GaeaApp {
1720
1772
 
1721
1773
  } // end else (externalDivId)
1722
1774
 
1723
- // 鎵撳垎绫绘爣璁帮紝鐢ㄤ簬鎸夌被鍒樉闅? if (category) {
1775
+ // 打分类标记,用于按类别显隐
1776
+ if (category) {
1724
1777
  container.classList.add('gaea-cat-' + category);
1725
1778
  }
1726
1779
 
@@ -1733,10 +1786,11 @@ export class GaeaApp {
1733
1786
  container.style.pointerEvents = 'auto';
1734
1787
  container.style.cursor = 'pointer';
1735
1788
 
1736
- // 鍦?DOM 涓婄粦鐐瑰嚮浜嬩欢锛圙aea ScreenGUIElement 鐨?OnHitElement 瀵?DOM 鍨嬪浘鏍囦笉鐢熸晥锛? container.addEventListener('click', (e) => {
1789
+ // 在 DOM 上绑点击事件(Gaea ScreenGUIElement 的 OnHitElement 对 DOM 型图标不生效)
1790
+ container.addEventListener('click', (e) => {
1737
1791
  e.stopPropagation();
1738
1792
  e.preventDefault();
1739
- console.log('[GaeaApp] 鍥炬爣鐐瑰嚮:', name);
1793
+ console.log('[GaeaApp] 图标点击:', name);
1740
1794
  if (onClick) { onClick(name, { text, position }); }
1741
1795
  else if (position) { this.flyToNode({ position }); }
1742
1796
  });
@@ -1761,7 +1815,7 @@ export class GaeaApp {
1761
1815
  screen.EnablePick = true;
1762
1816
  if (visibleRange.length) screen.VisibleRange = new Gaea.Vector2(visibleRange[0], visibleRange[1]);
1763
1817
 
1764
- // 濡傛灉鏈?elementName锛屽皢鍥炬爣鎸傚埌 GLTF 妯″瀷瀛愯妭鐐逛笂锛堜綅缃敱鐖惰妭鐐瑰喅瀹氾級
1818
+ // 如果有 elementName,将图标挂到 GLTF 模型子节点上(位置由父节点决定)
1765
1819
  if (elementName && elementChildrenIndex !== undefined) {
1766
1820
  const el = this._gltfElements?.[elementName];
1767
1821
  console.log('[GaeaApp] addScreenIcon element:', elementName, 'el:', !!el, 'childrenIndex:', elementChildrenIndex);
@@ -1778,7 +1832,8 @@ export class GaeaApp {
1778
1832
  return screen;
1779
1833
  }
1780
1834
  }
1781
- // 瀛愯妭鐐规湭鎵惧埌锛屽洖閫€鍒扮粷瀵逛綅缃? console.warn('[GaeaApp] elementName/childIndex 鏈壘鍒帮紝浣跨敤 position 鍥為€€:', elementName, elementChildrenIndex);
1835
+ // 子节点未找到,回退到绝对位置
1836
+ console.warn('[GaeaApp] elementName/childIndex 未找到,使用 position 回退:', elementName, elementChildrenIndex);
1782
1837
  }
1783
1838
 
1784
1839
  screen.GeographyPosition = new Gaea.Vector3(position.x, position.y, position.z || 0);
@@ -1789,7 +1844,7 @@ export class GaeaApp {
1789
1844
  return screen;
1790
1845
  }
1791
1846
 
1792
- /** 鏍规嵁鍏ㄥ眬鍥炬爣鍙鎬х姸鎬侊紙_markersVisible锛夊簲鐢ㄥ崟涓浘鏍囨樉闅?*/
1847
+ /** 根据全局图标可见性状态(_markersVisible)应用单个图标显隐 */
1793
1848
  _applyMarkerVisibility(screenItem) {
1794
1849
  if (this._markersVisible) return;
1795
1850
  screenItem.element.ForceSetVisible(false);
@@ -1798,28 +1853,30 @@ export class GaeaApp {
1798
1853
  if (el) el.style.display = 'none';
1799
1854
  }
1800
1855
 
1801
- /** 璁板綍褰撳墠瑙嗚锛岃繑鍥?URI 瀛楃涓?*/
1856
+ /** 记录当前视角,返回 URI 字符串 */
1802
1857
  favorite() {
1803
1858
  return Gaea.GaeaCamera.ConvertBy(Gaea.World.Instance.DefaultCamera).Favorite();
1804
1859
  }
1805
1860
 
1806
- /** 瀹氫綅鍒板凡璁板綍鐨勮瑙?URI */
1861
+ /** 定位到已记录的视角 URI */
1807
1862
  favoritePosition(uri) {
1808
1863
  Gaea.World.Instance.DefaultCamera.GotoFavorite(uri);
1809
1864
  }
1810
1865
 
1811
1866
  /**
1812
- * 娣诲姞鐐逛綅鏍囪鍒?3D 鍦板浘
1867
+ * 添加点位标记到 3D 地图
1813
1868
  * @param {Object} opts
1814
- * @param {string} opts.name - 鍞竴鏍囪瘑鍚? * @param {string} [opts.text] - 鏄剧ず鏂囨湰
1815
- * @param {number} opts.lat - 绾害
1816
- * @param {number} opts.lng - 缁忓害
1817
- * @param {number} [opts.z=0] - 楂樺害
1818
- * @param {string} [opts.imgUrl] - 鍥炬爣鍦板潃
1819
- * @param {'textImage'|'styled'|'imageOnly'} [opts.method='textImage'] - 鏄剧ず鏍峰紡
1820
- * @param {Array<number>} [opts.offset=[0,0]] - 灞忓箷鍋忕Щ
1821
- * @param {Function} [opts.onClick] - 鐐瑰嚮鍥炶皟
1822
- * @param {string} [opts.category] - 鍒嗙被鏍囪瘑锛堢敤浜庡浘灞傛爲鏄鹃殣鎺у埗锛? * @returns {Gaea.ScreenGUIElement} screen 鍏冪礌
1869
+ * @param {string} opts.name - 唯一标识名
1870
+ * @param {string} [opts.text] - 显示文本
1871
+ * @param {number} opts.lat - 纬度
1872
+ * @param {number} opts.lng - 经度
1873
+ * @param {number} [opts.z=0] - 高度
1874
+ * @param {string} [opts.imgUrl] - 图标地址
1875
+ * @param {'textImage'|'styled'|'imageOnly'} [opts.method='textImage'] - 显示样式
1876
+ * @param {Array<number>} [opts.offset=[0,0]] - 屏幕偏移
1877
+ * @param {Function} [opts.onClick] - 点击回调
1878
+ * @param {string} [opts.category] - 分类标识(用于图层树显隐控制)
1879
+ * @returns {Gaea.ScreenGUIElement} screen 元素
1823
1880
  */
1824
1881
  addMarker({ name, text, lat, lng, z = 0, imgUrl, method = 'textImage', offset = [0, 0], onClick, category } = {}) {
1825
1882
  return this.addScreenIcon({
@@ -1830,7 +1887,7 @@ export class GaeaApp {
1830
1887
  method,
1831
1888
  offset,
1832
1889
  onClick: (clickedName, data) => {
1833
- // 娲惧彂鍏ㄥ眬鑷畾涔変簨浠讹紝鐖剁粍浠跺彲閫氳繃 document.addEventListener 鐩戝惉
1890
+ // 派发全局自定义事件,父组件可通过 document.addEventListener 监听
1834
1891
  document.dispatchEvent(new CustomEvent('gaea-marker-click', {
1835
1892
  detail: { name: clickedName, text: data.text, position: data.position, category },
1836
1893
  bubbles: true
@@ -1842,7 +1899,7 @@ export class GaeaApp {
1842
1899
  })
1843
1900
  }
1844
1901
 
1845
- // ---- 寮曟搸鍔ㄧ敾鏂规硶 ----
1902
+ // ---- 引擎动画方法 ----
1846
1903
 
1847
1904
  hiddenScreenWithClass(className) {
1848
1905
  const els = document.querySelectorAll(className);
@@ -1883,15 +1940,15 @@ export class GaeaApp {
1883
1940
  if (details.length) {
1884
1941
  details.forEach(item => { html += `<div class="daba-tooltip-item">${item}</div>`; });
1885
1942
  } else {
1886
- html = '<div class="daba-tooltip-item">鏆傛棤璇︽儏</div>';
1943
+ html = '<div class="daba-tooltip-item">暂无详情</div>';
1887
1944
  }
1888
1945
  return `<div class="daba-tooltip-title">${name} - ${label}</div>${html}`;
1889
1946
  }
1890
1947
 
1891
- /** 鏂潰鏁版嵁鐐瑰嚮澶勭悊 */
1948
+ /** 断面数据点击处理 */
1892
1949
  _onSecDataClick(name, data) {
1893
1950
  const sectionModel = this._gltfElements?.[name];
1894
- const tourModelName = name + '娓歌妯″瀷';
1951
+ const tourModelName = name + '游览模型';
1895
1952
  const tourModel = this._gltfElements?.[tourModelName];
1896
1953
 
1897
1954
  document.dispatchEvent(new CustomEvent('gaea-icon-click', {
@@ -1904,7 +1961,8 @@ export class GaeaApp {
1904
1961
  return;
1905
1962
  }
1906
1963
 
1907
- // 棣栨鐐瑰嚮鏂潰鏃舵敞鍏ユ爣绛炬牱寮? if (!document.getElementById('gaea-sec-styles')) {
1964
+ // 首次点击断面时注入标签样式
1965
+ if (!document.getElementById('gaea-sec-styles')) {
1908
1966
  const style = document.createElement('style');
1909
1967
  style.id = 'gaea-sec-styles';
1910
1968
  style.textContent = `
@@ -1932,17 +1990,20 @@ export class GaeaApp {
1932
1990
  this.updateElementPosition({ element: tourModel, position: gltfCfg.position, rotate: gltfCfg.rotate, size: gltfCfg.size });
1933
1991
  }
1934
1992
 
1935
- // this.RemoveVts({ vtsName: '娌挎郸娴峰宸ョ▼鑼冨洿绾? });
1936
- // 閫氱煡闅愯棌鍥惧眰鏍? document.dispatchEvent(new CustomEvent('gaea-hide-layertree', { bubbles: true }));
1993
+ // this.RemoveVts({ vtsName: '沿浦海塘工程范围线' });
1994
+ // 通知隐藏图层树
1995
+ document.dispatchEvent(new CustomEvent('gaea-hide-layertree', { bubbles: true }));
1937
1996
  this.FlyOver(() => {
1938
1997
  this.hiddenScreenWithClass('.waterLevel');
1939
1998
  this.hiddenScreenWithClass('.damScreen');
1940
1999
 
1941
2000
  this.gltfPlayer(sectionModel, { x: 0, y: 44, z: 0 }, 1).then(() => {
1942
- // 璁板綍宸插崌璧风殑鏂潰妯″瀷锛屼緵閲嶇疆鏃剁缉鍥? this._raisedSection = sectionModel;
2001
+ // 记录已升起的断面模型,供重置时缩回
2002
+ this._raisedSection = sectionModel;
1943
2003
  if (tourModel) {
1944
2004
  this.lockedModesSwitch(true, tourModel, [-10, 50, 0], [50, 50]);
1945
- // 閿佸畾妯″瀷鍚庡啀閫氱煡鏄剧ず鏂潰婊氬姩鏉? document.dispatchEvent(new CustomEvent('gaea-sec-locked', { detail: { name }, bubbles: true }));
2005
+ // 锁定模型后再通知显示断面滚动条
2006
+ document.dispatchEvent(new CustomEvent('gaea-sec-locked', { detail: { name }, bubbles: true }));
1946
2007
  }
1947
2008
 
1948
2009
  if (!this._secIsClickArr.includes(name)) {
@@ -1960,9 +2021,9 @@ export class GaeaApp {
1960
2021
  const entryLabel = typeof value === 'object' && value.label ? value.label : String(value);
1961
2022
  let cls, imgUrl, offset;
1962
2023
  if (hasNumber(entryLabel)) {
1963
- cls = 'waterLevel'; offset = [0, -54]; imgUrl = '/image/labelImg/姘翠綅.png';
2024
+ cls = 'waterLevel'; offset = [0, -54]; imgUrl = '/image/labelImg/水位.png';
1964
2025
  } else {
1965
- cls = 'damScreen'; offset = [0, -19]; imgUrl = '/image/labelImg/宸﹁竟.png';
2026
+ cls = 'damScreen'; offset = [0, -19]; imgUrl = '/image/labelImg/左边.png';
1966
2027
  }
1967
2028
  const divid = this._createSecLabel(entryLabel, cls, imgUrl, {
1968
2029
  onMouseEnter: (e) => {
@@ -1995,12 +2056,12 @@ export class GaeaApp {
1995
2056
  this.hiddenScreenWithClass('.gaea-iconscreen');
1996
2057
  this.hiddenScreenWithClass('.gaea-locationLineStyle');
1997
2058
  this.hiddenScreenWithClass('.gaea-iconGif');
1998
- }).catch(err => console.warn('[GaeaApp] 鏂潰鍔ㄧ敾澶辫触:', err.message));
2059
+ }).catch(err => console.warn('[GaeaApp] 断面动画失败:', err.message));
1999
2060
  });
2000
2061
  this.SetPosition(data.favorite || `${data.position.x},${data.position.y},${data.position.z},${data.position.z + 500},0,0`);
2001
2062
  }
2002
2063
 
2003
- /** 椋炶缁撴潫鍥炶皟 */
2064
+ /** 飞行结束回调 */
2004
2065
  FlyOver(callback) {
2005
2066
  const camera = Gaea.World.Instance.DefaultCamera;
2006
2067
  camera.OnFlyOverCallBack(() => {
@@ -2008,15 +2069,15 @@ export class GaeaApp {
2008
2069
  });
2009
2070
  }
2010
2071
 
2011
- /** GLTF 妯″瀷骞崇Щ鍔ㄧ敾 */
2072
+ /** GLTF 模型平移动画 */
2012
2073
  gltfPlayer = (element, targetPos, time = 5) => {
2013
2074
  return new Promise((resolve, reject) => {
2014
2075
  if (!element || !element.TranslateObjectLocal) {
2015
- reject(new Error('鏃犳晥鐨勫厓绱犲璞★細缂哄皯 TranslateObjectLocal 鏂规硶'));
2076
+ reject(new Error('无效的元素对象:缺少 TranslateObjectLocal 方法'));
2016
2077
  return;
2017
2078
  }
2018
2079
  if (!targetPos || typeof targetPos.x !== 'number' || typeof targetPos.y !== 'number' || typeof targetPos.z !== 'number') {
2019
- reject(new Error('鏃犳晥鐨勭洰鏍囦綅缃細蹇呴』鍖呭惈鏁板瓧绫诲瀷鐨?x/y/z 灞炴€?));
2080
+ reject(new Error('无效的目标位置:必须包含数字类型的 x/y/z 属性'));
2020
2081
  return;
2021
2082
  }
2022
2083
  const frameRate = 60;
@@ -2053,12 +2114,12 @@ export class GaeaApp {
2053
2114
  };
2054
2115
  moveStep();
2055
2116
  } catch (error) {
2056
- reject(new Error(`鍔ㄧ敾鎵ц鍑洪敊锛?{error.message}`));
2117
+ reject(new Error(`动画执行出错:${error.message}`));
2057
2118
  }
2058
2119
  });
2059
2120
  };
2060
2121
 
2061
- /** 閿佸畾/瑙i攣鐩告満瑙嗚鍒版ā鍨?*/
2122
+ /** 锁定/解锁相机视角到模型 */
2062
2123
  lockedModesSwitch = (mode, element, Offset3D = [0, 1.1, 0], MinMaxZoom3D = [1.1, 1.1], LockEye = false) => {
2063
2124
  if (mode) {
2064
2125
  const ev = new Gaea.EntityView();
@@ -2072,7 +2133,8 @@ export class GaeaApp {
2072
2133
  return ev;
2073
2134
  } else {
2074
2135
  Gaea.World.Instance.CameraController.LockedEntity = null;
2075
- // 瑙i攣鏃舵仮澶嶉潪鏂潰鍥炬爣鐨勮嚜鍔ㄥ彲瑙? this._screenIconList.forEach(item => {
2136
+ // 解锁时恢复非断面图标的自动可见
2137
+ this._screenIconList.forEach(item => {
2076
2138
  if (!item.type && this._markersVisible) {
2077
2139
  item.element.UseAutoVisible();
2078
2140
  item.element.EnablePick = true;
@@ -2081,7 +2143,7 @@ export class GaeaApp {
2081
2143
  }
2082
2144
  };
2083
2145
 
2084
- /** 淇敼妯″瀷浣嶇疆/鏃嬭浆 */
2146
+ /** 修改模型位置/旋转 */
2085
2147
  updateElementPosition = ({ element, position, rotate, size }) => {
2086
2148
  element.GeographyPosition = new Gaea.Vector3(position.x, position.y, position.z);
2087
2149
  if (rotate?.length > 0) {
@@ -2089,14 +2151,14 @@ export class GaeaApp {
2089
2151
  }
2090
2152
  };
2091
2153
 
2092
- /** 绉婚櫎 VTS 鐭㈤噺鍥惧眰 */
2154
+ /** 移除 VTS 矢量图层 */
2093
2155
  RemoveVts = ({ vtsName }) => {
2094
2156
  Gaea.World.Instance.DefaultVectorPyramidTileSet.RemoveImageStore(
2095
2157
  Gaea.WMTSVectorStore.ConvertBy(Gaea.World.Instance.DefaultVectorPyramidTileSet.GetImageStore(vtsName))
2096
2158
  );
2097
2159
  };
2098
2160
 
2099
- /** GLTF 杞ㄨ抗鍔ㄧ敾鎾斁锛堢敤浜庢柇闈㈡父瑙堬級 */
2161
+ /** GLTF 轨迹动画播放(用于断面游览) */
2100
2162
  gtlfAnimationPlayer = (trackList, animationConfig, element) => {
2101
2163
  element.GeographyPosition = new Gaea.Vector3(trackList[0].x, trackList[0].y, trackList[0].z);
2102
2164
  const { animationName, animationType, animationPlaySpeed } = animationConfig;
@@ -2123,7 +2185,7 @@ export class GaeaApp {
2123
2185
  return animationPlayer;
2124
2186
  };
2125
2187
 
2126
- /** 鐜粫椋炶 */
2188
+ /** 环绕飞行 */
2127
2189
  FlyAroundPoint = (element, targetAngle, time, callbacks = {}) => {
2128
2190
  const { onStart = () => {}, onUpdate = () => {}, onComplete = () => {} } = callbacks;
2129
2191
  if (!targetAngle || !element) { onComplete(null); return; }
@@ -2145,7 +2207,7 @@ export class GaeaApp {
2145
2207
  tween.start();
2146
2208
  };
2147
2209
 
2148
- /** 鍚姩 TWEEN 鏇存柊寰幆锛堥娆¤皟鐢ㄦ椂鍚姩锛?*/
2210
+ /** 启动 TWEEN 更新循环(首次调用时启动) */
2149
2211
  _startTweenLoop() {
2150
2212
  if (this._tweenLoopStarted) return;
2151
2213
  this._tweenLoopStarted = true;
@@ -2156,7 +2218,7 @@ export class GaeaApp {
2156
2218
  requestAnimationFrame(animate);
2157
2219
  }
2158
2220
 
2159
- /** 鎻愬彇绱㈠紩鍖洪棿鐨?pos 鏁扮粍锛岃捣濮?鐩爣鏃跺弽搴?*/
2221
+ /** 提取索引区间的 pos 数组,起始>目标时反序 */
2160
2222
  getPosArrayBetweenIndexes(list, currentIdx, targetIdx) {
2161
2223
  if (!Array.isArray(list) || currentIdx < 0 || targetIdx < 0 ||
2162
2224
  currentIdx >= list.length || targetIdx >= list.length) {
@@ -2170,20 +2232,22 @@ export class GaeaApp {
2170
2232
  return result;
2171
2233
  }
2172
2234
 
2173
- /** 瀹ゅ唴娓歌鍒濆鍖栵細闅愯棌鍥炬爣 + 閿佸畾娓歌妯″瀷 */
2235
+ /** 室内游览初始化:隐藏图标 + 锁定游览模型 */
2174
2236
  initModelTour(modelName, direction = [-0.5, 0, 0]) {
2175
- // 闅愯棌鎵€鏈夊睆骞曞浘鏍? this._screenIconList.forEach(item => {
2237
+ // 隐藏所有屏幕图标
2238
+ this._screenIconList.forEach(item => {
2176
2239
  item.element.ForceSetVisible(false);
2177
2240
  item.element.EnablePick = false;
2178
2241
  });
2179
2242
  this.hiddenScreenWithClass('.gaea-locationLineStyle');
2180
2243
  this.hiddenScreenWithClass('.gaea-iconscreen');
2181
2244
  this.hiddenScreenWithClass('.gaea-iconGif');
2182
- // 闅愯棌鍥惧眰鏍? document.dispatchEvent(new CustomEvent('gaea-hide-layertree', { bubbles: true }));
2245
+ // 隐藏图层树
2246
+ document.dispatchEvent(new CustomEvent('gaea-hide-layertree', { bubbles: true }));
2183
2247
 
2184
- const tourModel = this._gltfElements?.[`${modelName}娓歌妯″瀷`];
2248
+ const tourModel = this._gltfElements?.[`${modelName}游览模型`];
2185
2249
  if (!tourModel) {
2186
- console.warn('[GaeaApp] 娓歌妯″瀷鏈壘鍒?', modelName);
2250
+ console.warn('[GaeaApp] 游览模型未找到:', modelName);
2187
2251
  return null;
2188
2252
  }
2189
2253
  this._currentTourModel = tourModel;
@@ -2192,7 +2256,7 @@ export class GaeaApp {
2192
2256
  return tourModel;
2193
2257
  }
2194
2258
 
2195
- /** 瀹ゅ唴娓歌璺緞鑺傜偣鐐瑰嚮锛氭部璺緞绉诲姩 + 杞ご */
2259
+ /** 室内游览路径节点点击:沿路径移动 + 转头 */
2196
2260
  tourNodeClick(positionList, index) {
2197
2261
  const tourModel = this._currentTourModel;
2198
2262
  if (!tourModel) return;
@@ -2201,7 +2265,7 @@ export class GaeaApp {
2201
2265
  const currentPosList = this.getPosArrayBetweenIndexes(positionList, currentIdx, index);
2202
2266
  const time = 2 * (currentPosList.length - 1);
2203
2267
  this.gtlfAnimationPlayer(currentPosList, {
2204
- animationName: `鍔ㄧ敾${index}`, animationType: 'time', animationPlaySpeed: time, animationLoop: false,
2268
+ animationName: `动画${index}`, animationType: 'time', animationPlaySpeed: time, animationLoop: false,
2205
2269
  }, tourModel);
2206
2270
  const status = currentIdx > index ? 'out' : 'in';
2207
2271
  this._currentTourIndexPos = index;
@@ -2222,7 +2286,7 @@ export class GaeaApp {
2222
2286
  }, time * 1000);
2223
2287
  }
2224
2288
 
2225
- /** 閫氳繃 URL 鍔犺浇璧勬簮 */
2289
+ /** 通过 URL 加载资源 */
2226
2290
  LoadResourceFromUrl = (url, name) => {
2227
2291
  return new Promise((resolve, reject) => {
2228
2292
  Gaea.GaeaResourceLoader.Instance.LoadResourceFromUrl1(url, name, (code, msg) => {
@@ -2232,20 +2296,22 @@ export class GaeaApp {
2232
2296
  });
2233
2297
  };
2234
2298
 
2235
- /** 娣诲姞鍗曚釜鐐瑰浘鏍?*/
2299
+ /** 添加单个点图标 */
2236
2300
  addPointIcon = async (options = {}) => {
2237
2301
  const { lat, lng, alt = 0, iconSize = 500, enablePick = true } = options;
2238
2302
  if (lat == null || lng == null) {
2239
- console.error('addPointIcon: lat 鍜?lng 涓嶈兘涓虹┖');
2303
+ console.error('addPointIcon: lat 和 lng 不能为空');
2240
2304
  return null;
2241
2305
  }
2242
- // 璧勬簮鍚嶅甫鐗堟湰鍙凤細npm 鍖呮棭鏈熼檮甯︾殑 1111.res 涓嶅畬鏁达紝浼氳寮曟搸鎸夊悕缂撳瓨杩涙寔涔呭寲鏂囦欢绯荤粺锛? // 鎹㈡柊鍚嶅彲寮哄埗閲嶆柊涓嬭浇娉ㄥ唽锛岄伩鍏嶅懡涓棫鐨勬棤鏁堢紦瀛? const resName = 'pointIconV3.res';
2306
+ // 资源名带版本号:npm 包早期附带的 1111.res 不完整,会被引擎按名缓存进持久化文件系统,
2307
+ // 换新名可强制重新下载注册,避免命中旧的无效缓存
2308
+ const resName = 'pointIconV3.res';
2243
2309
  if (!this._pointIconLoaded) {
2244
2310
  const resUrl = `${this.config.serveLocal}/1111.res?v=3`;
2245
2311
  try {
2246
2312
  await this.LoadResourceFromUrl(resUrl, resName);
2247
2313
  } catch (e) {
2248
- console.error('addPointIcon: 鍔犺浇 .res 鍥炬爣鏂囦欢澶辫触');
2314
+ console.error('addPointIcon: 加载 .res 图标文件失败');
2249
2315
  return null;
2250
2316
  }
2251
2317
  this._pointIconLoaded = true;
@@ -2269,7 +2335,7 @@ export class GaeaApp {
2269
2335
  return element;
2270
2336
  };
2271
2337
 
2272
- /** 娓呴櫎鎵€鏈夌偣鍥炬爣 */
2338
+ /** 清除所有点图标 */
2273
2339
  clearPointIcons = () => {
2274
2340
  if (this._pointIconElements && this._pointIconElements.length > 0) {
2275
2341
  this._pointIconElements.forEach
@@ -2279,17 +2345,17 @@ export class GaeaApp {
2279
2345
  this.clearTyphoonRoute();
2280
2346
  };
2281
2347
 
2282
- /** 鍔犺浇鍙伴璺嚎锛氬垱寤哄浘鏍囧苟娌胯矾寰勮繍鍔紙鐐逛綅鏁扮粍 [{lat,lng,alt}]锛?*/
2348
+ /** 加载台风路线:创建图标并沿路径运动(点位数组 [{lat,lng,alt}]) */
2283
2349
  addTyphoonRoute = async (pointList = [], options = {}) => {
2284
2350
  const { animationPlaySpeed = 20, animationLoop = true } = options;
2285
2351
  this.clearTyphoonRoute();
2286
2352
  if (!pointList || pointList.length < 2) {
2287
- console.warn('addTyphoonRoute: 鑷冲皯闇€瑕?涓偣浣?);
2353
+ console.warn('addTyphoonRoute: 至少需要2个点位');
2288
2354
  return;
2289
2355
  }
2290
2356
  const element = await this.addPointIcon({ lat: pointList[0].lat, lng: pointList[0].lng, alt: pointList[0].alt || 0 });
2291
2357
  if (!element) {
2292
- console.error('addTyphoonRoute: 鍒涘缓鍥炬爣澶辫触');
2358
+ console.error('addTyphoonRoute: 创建图标失败');
2293
2359
  return;
2294
2360
  }
2295
2361
  if (this._pointIconElements) {
@@ -2339,7 +2405,7 @@ export class GaeaApp {
2339
2405
  return { element };
2340
2406
  };
2341
2407
 
2342
- /** 娓呴櫎鍙伴璺嚎 */
2408
+ /** 清除台风路线 */
2343
2409
  clearTyphoonRoute = () => {
2344
2410
  this._typhoonCancelled = true;
2345
2411
  if (this._typhoonTimer) { clearTimeout(this._typhoonTimer); this._typhoonTimer = null; }
@@ -2349,42 +2415,44 @@ export class GaeaApp {
2349
2415
  }
2350
2416
  };
2351
2417
 
2352
- /** 鏄剧ず鎵€鏈夊睆骞曟爣璁?*/
2418
+ /** 显示所有屏幕标记 */
2353
2419
  showAllMarkers = () => {
2354
2420
  this._markersVisible = true;
2355
2421
  this._screenIconList.forEach(item => {
2356
- // 璺宠繃琚殣钘忓垎绫荤殑鏍囪
2422
+ // 跳过被隐藏分类的标记
2357
2423
  if (this._hiddenCategories.has(item.category)) return
2358
2424
  item.element.UseAutoVisible()
2359
2425
  item.element.EnablePick = true
2360
- // 鍚屾鏄剧ず DOM 鍏冪礌
2426
+ // 同步显示 DOM 元素
2361
2427
  const el = document.getElementById(item.element.DivID)
2362
2428
  if (el) el.style.display = ''
2363
2429
  })
2364
2430
  };
2365
2431
 
2366
- /** 闅愯棌鎵€鏈夊睆骞曟爣璁?*/
2432
+ /** 隐藏所有屏幕标记 */
2367
2433
  hideAllMarkers = () => {
2368
2434
  this._markersVisible = false;
2369
2435
  this._screenIconList.forEach(item => {
2370
2436
  item.element.ForceSetVisible(false)
2371
2437
  item.element.EnablePick = false
2372
- // 鍚屾闅愯棌 DOM 鍏冪礌
2438
+ // 同步隐藏 DOM 元素
2373
2439
  const el = document.getElementById(item.element.DivID)
2374
2440
  if (el) el.style.display = 'none'
2375
2441
  })
2376
2442
  };
2377
2443
 
2378
- // ---- 姘撮潰鍔犺浇 ----
2444
+ // ---- 水面加载 ----
2379
2445
 
2380
2446
  /**
2381
- * 鍔犺浇姘撮潰
2447
+ * 加载水面
2382
2448
  * @param {Object} opts
2383
- * @param {string} opts.gltfUrl - GLTF 妯″瀷璺緞
2384
- * @param {Object} opts.Position - { x: 绾害, y: 缁忓害, z: 楂樼▼ }
2385
- * @param {Array<number>} opts.rotate - [tilt, heading, bank] 鏃嬭浆瑙掑害
2386
- * @param {Array<number>} opts.scale - [sx, sy, sz] 缂╂斁
2387
- * @param {Array<number>} [opts.colorDeep=[0,157,187]] - 娣辨按鍖洪鑹?[r, g, b]锛?-255锛? * @param {number} [opts.flowSpeed=0.1] - 娴侀€? */
2449
+ * @param {string} opts.gltfUrl - GLTF 模型路径
2450
+ * @param {Object} opts.Position - { x: 纬度, y: 经度, z: 高程 }
2451
+ * @param {Array<number>} opts.rotate - [tilt, heading, bank] 旋转角度
2452
+ * @param {Array<number>} opts.scale - [sx, sy, sz] 缩放
2453
+ * @param {Array<number>} [opts.colorDeep=[0,157,187]] - 深水区颜色 [r, g, b](0-255)
2454
+ * @param {number} [opts.flowSpeed=0.1] - 流速
2455
+ */
2388
2456
  addWaterHandle = async ({
2389
2457
  gltfUrl, Position, rotate, scale,
2390
2458
  colorDeep = [0, 157, 187],
@@ -2414,22 +2482,26 @@ export class GaeaApp {
2414
2482
  Gaea.World.Instance.SetMtlColor(material, 'color_shallow', new Gaea.Color(0 / 255, 157 / 255, 187 / 255, 1))
2415
2483
  Gaea.World.Instance.SetMtlColor(material, 'color_deep', new Gaea.Color(colorDeep[0] / 255, colorDeep[1] / 255, colorDeep[2] / 255, 1))
2416
2484
  Gaea.World.Instance.SetMtlFloat(material, 'transparency_clarity', 0)
2417
- Gaea.World.Instance.SetMtlFloat(material, "transparency_refraction", 0); // 鎶樺皠搴? Gaea.World.Instance.SetMtlFloat(material, "foam_amount", 0.8); // 姘旀场澶у皬
2485
+ Gaea.World.Instance.SetMtlFloat(material, "transparency_refraction", 0); // 折射度
2486
+ Gaea.World.Instance.SetMtlFloat(material, "foam_amount", 0.8); // 气泡大小
2418
2487
  })
2419
2488
 
2420
2489
  Gaea.World.Instance.RenderableObjectList.AddLast(element)
2421
2490
  return element
2422
2491
  };
2423
2492
 
2424
- // ---- 寮€闂ㄥ姩鐢?----
2493
+ // ---- 开门动画 ----
2425
2494
 
2426
2495
  /**
2427
- * 妯″瀷缂撳姩鍔ㄧ敾锛堢缉鏀?浣嶇Щ/鏃嬭浆锛? * @param {Object} element - GLTF 鍏冪礌
2428
- * @param {Object} Scale - 鐩爣缂╂斁 { x, y, z }
2429
- * @param {Object} Position - 鐩爣浣嶇疆 { x, y, z }
2430
- * @param {Object} translation - 妯″瀷鐩稿浣嶇Щ { x, y, z }
2431
- * @param {number} time - 鍔ㄧ敾鏃堕棿锛坢s锛? * @param {Object} Door - 闂ㄦ棆杞搴?{ x, y, z }
2432
- * @param {number} floor - 闂ㄥ湪妯″瀷鏍戜腑鐨勫眰绾х储寮? */
2496
+ * 模型缓动动画(缩放/位移/旋转)
2497
+ * @param {Object} element - GLTF 元素
2498
+ * @param {Object} Scale - 目标缩放 { x, y, z }
2499
+ * @param {Object} Position - 目标位置 { x, y, z }
2500
+ * @param {Object} translation - 模型相对位移 { x, y, z }
2501
+ * @param {number} time - 动画时间(ms)
2502
+ * @param {Object} Door - 门旋转角度 { x, y, z }
2503
+ * @param {number} floor - 门在模型树中的层级索引
2504
+ */
2433
2505
  tweentest = (element, Scale = {}, Position = {}, translation = {}, time, Door = {}, floor = 0) => {
2434
2506
  const { Tween, Easing } = window.TWEEN
2435
2507
 
@@ -2489,14 +2561,15 @@ export class GaeaApp {
2489
2561
  }
2490
2562
  };
2491
2563
 
2492
- // ---- 妯″瀷鍐呯疆鍔ㄧ敾 ----
2564
+ // ---- 模型内置动画 ----
2493
2565
 
2494
2566
  /**
2495
- * 鎵ц妯″瀷鍐呯疆鍔ㄧ敾锛堝寮€闂?鍏抽椄鍔ㄧ敾锛? * @param {Object} opts
2496
- * @param {string} opts.animationPath - 鍔ㄧ敾璺緞锛屽 '娌挎郸姘撮椄/AnimationPlayer'
2497
- * @param {string} opts.animationName - 鍔ㄧ敾鍚嶇О锛屽 '寮€闂?
2498
- * @param {number} [opts.speed=1.0] - 鍔ㄧ敾閫熷害
2499
- * @param {boolean} [opts.isReversePlay=false] - 鏄惁鍙嶅悜鎾斁
2567
+ * 执行模型内置动画(如开闸/关闸动画)
2568
+ * @param {Object} opts
2569
+ * @param {string} opts.animationPath - 动画路径,如 '沿浦水闸/AnimationPlayer'
2570
+ * @param {string} opts.animationName - 动画名称,如 '开闸'
2571
+ * @param {number} [opts.speed=1.0] - 动画速度
2572
+ * @param {boolean} [opts.isReversePlay=false] - 是否反向播放
2500
2573
  */
2501
2574
  executeModelAnimation = ({ animationPath, animationName, speed = 1.0, isReversePlay = false }) => {
2502
2575
  const node = Gaea.World.Instance.GetNode(animationPath)