@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.
- package/CHANGELOG.md +6 -0
- package/package.json +1 -1
- package/src/GaeaMethods.js +541 -468
package/src/GaeaMethods.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
2
|
* gaea-3dview npm package
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
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
|
-
/*
|
|
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
|
-
/**
|
|
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
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
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
|
|
137
|
+
// dev 开发:引擎资源走 vite dev server 中间件(本地 gaea public),无需访问线上
|
|
133
138
|
if (import.meta.env.DEV) return '/Gaea';
|
|
134
|
-
//
|
|
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
|
-
/*
|
|
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
|
-
*
|
|
154
|
-
* '
|
|
158
|
+
* 服务模式:
|
|
159
|
+
* 'platform' - 服务平台:影像走 serveUrl 的 WMTS,并加载地形、贴地线
|
|
160
|
+
* 'online' - 在线:仅加载天地图影像,不加载地形和贴地线
|
|
161
|
+
*/
|
|
155
162
|
serviceMode: 'platform',
|
|
156
|
-
/**
|
|
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: '
|
|
170
|
+
{ layerName: '苍南L19', forMat: 'image/png', min: 5, max: 17 },
|
|
164
171
|
],
|
|
165
|
-
/**
|
|
172
|
+
/** 默认加载的地形服务(走 serveUrl,url 为相对路径或完整地址;空数组则不加载) */
|
|
166
173
|
defaultTerrains: [
|
|
167
|
-
{ name: '
|
|
174
|
+
{ name: '苍南县地形', url: 'htc/service/tms/1.0.0/苍南县地形@EPSG%3A4326@terrain/' },
|
|
168
175
|
],
|
|
169
|
-
/**
|
|
176
|
+
/** 默认加载的贴地线(矢量线服务,依赖 WMTS 服务列表;空数组则不加载) */
|
|
170
177
|
defaultGroundLines: [
|
|
171
|
-
{ name: '
|
|
172
|
-
{ name: '
|
|
173
|
-
{ name: '
|
|
174
|
-
{ name: '
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
195
|
+
/** 默认隐藏的分类 key 列表,如 ['rainStations', 'rescue'] */
|
|
189
196
|
defaultHiddenCategories: [],
|
|
190
|
-
/**
|
|
197
|
+
/** 图层树分类数据(默认数据,接口数据会合并进来) */
|
|
191
198
|
layerCategories: [
|
|
192
|
-
{ key: 'seawall', text: '
|
|
193
|
-
{ text: '
|
|
194
|
-
{ text: '
|
|
195
|
-
{ text: '
|
|
196
|
-
{ text: '
|
|
197
|
-
{ text: '
|
|
198
|
-
{ text: '
|
|
199
|
-
{ text: '
|
|
200
|
-
{ text: '
|
|
201
|
-
{ text: '
|
|
202
|
-
{ text: '
|
|
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: '
|
|
205
|
-
{ text: '
|
|
206
|
-
{ text: '
|
|
207
|
-
{ text: '
|
|
208
|
-
{ text: '
|
|
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: '
|
|
211
|
-
{ key: 'riverStations', text: '
|
|
212
|
-
{ key: 'rainStations', text: '
|
|
213
|
-
{ key: 'reservoirs', text: '
|
|
214
|
-
{ key: 'sluice', text: '
|
|
215
|
-
{ text: '
|
|
216
|
-
{ text: '
|
|
217
|
-
{ text: '
|
|
218
|
-
{ text: '
|
|
219
|
-
{ text: '
|
|
220
|
-
{ text: '
|
|
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: '
|
|
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: '
|
|
226
|
-
2: { label: '
|
|
227
|
-
3: { label: '6.89', details: ['
|
|
228
|
-
4: { label: '6.00', details: ['
|
|
229
|
-
5: { label: '
|
|
230
|
-
6: { label: '3.00', details: ['
|
|
231
|
-
7: { label: '
|
|
232
|
-
8: { label: '2.00', details: ['
|
|
233
|
-
9: { label: '3.94
|
|
234
|
-
10: { label: '4.5', details: ['
|
|
235
|
-
11: { label: '50
|
|
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: '
|
|
242
|
-
2: { label: '
|
|
243
|
-
3: { label: '
|
|
244
|
-
4: { label: '6.98', details: ['
|
|
245
|
-
5: { label: '6.00', details: ['
|
|
246
|
-
6: { label: '3.94', details: ['
|
|
247
|
-
7: { label: '2.00', details: ['
|
|
248
|
-
8: { label: '4.5', details: ['
|
|
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: '
|
|
255
|
-
1: { label: '
|
|
256
|
-
2: { label: '
|
|
257
|
-
3: { label: '6.98', details: ['
|
|
258
|
-
4: { label: '6.00', details: ['
|
|
259
|
-
5: { label: '3.94', details: ['
|
|
260
|
-
6: { label: '2.00', details: ['
|
|
261
|
-
7: { label: '4.5', details: ['
|
|
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: '
|
|
268
|
-
1: { label: '
|
|
269
|
-
2: { label: '7.00', details: ['
|
|
270
|
-
3: { label: '6.00', details: ['
|
|
271
|
-
4: { label: '3.94', details: ['
|
|
272
|
-
5: { label: '2.00', details: ['
|
|
273
|
-
6: { label: '4.5', details: ['
|
|
274
|
-
7: { label: '
|
|
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: '
|
|
280
|
-
{ key: 'rescue', text: '
|
|
281
|
-
{ text: '
|
|
282
|
-
{ text: '
|
|
283
|
-
{ text: '
|
|
284
|
-
{ text: '
|
|
285
|
-
{ text: '
|
|
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: '
|
|
288
|
-
{ text: '
|
|
289
|
-
{ text: '
|
|
290
|
-
{ text: '
|
|
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]
|
|
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; //
|
|
319
|
+
this._markersVisible = true; // 全局图标可见性(2D 模式下强制隐藏,3D 下恢复)
|
|
313
320
|
this._hiddenCategories = new Set();
|
|
314
|
-
this._tilesetMap = {}; //
|
|
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
|
-
'
|
|
321
|
-
{ name: '
|
|
322
|
-
{ name: '
|
|
323
|
-
{ name: '
|
|
324
|
-
{ name: '
|
|
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
|
-
'
|
|
327
|
-
{ name: '
|
|
328
|
-
{ name: '
|
|
329
|
-
{ name: '
|
|
330
|
-
{ name: '
|
|
331
|
-
{ name: '
|
|
332
|
-
{ name: '
|
|
333
|
-
{ name: '
|
|
334
|
-
{ name: '
|
|
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
|
-
'
|
|
337
|
-
{ name: '
|
|
338
|
-
{ name: '
|
|
339
|
-
{ name: '
|
|
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
|
-
'
|
|
342
|
-
'
|
|
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
|
-
//
|
|
354
|
+
// 显示 loading
|
|
348
355
|
showLoading();
|
|
349
356
|
|
|
350
|
-
//
|
|
357
|
+
// 自动启动初始化,catch 防止未处理的 Promise rejection
|
|
351
358
|
this.ready = this._autoInit().catch(err => {
|
|
352
|
-
console.error('[GaeaApp]
|
|
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.
|
|
370
|
+
// 1. 等 canvas 就绪
|
|
364
371
|
await this._waitForCanvas();
|
|
365
|
-
console.log('[GaeaApp] canvas
|
|
372
|
+
console.log('[GaeaApp] canvas 就绪');
|
|
366
373
|
|
|
367
|
-
// 2.
|
|
374
|
+
// 2. 启动 Godot 引擎
|
|
368
375
|
await this._startEngine();
|
|
369
|
-
console.log('[GaeaApp]
|
|
376
|
+
console.log('[GaeaApp] 引擎启动完成');
|
|
370
377
|
|
|
371
|
-
// 3.
|
|
372
|
-
|
|
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
|
-
//
|
|
386
|
+
// 影像/地形就绪即可显示地图,隐藏 loading,避免用户等待模型/接口
|
|
379
387
|
hideLoading();
|
|
380
388
|
this._addResetButton();
|
|
381
389
|
this._addLayerTreeButton();
|
|
382
|
-
console.log('[GaeaApp]
|
|
390
|
+
console.log('[GaeaApp] 地图已就绪,模型与图层树数据后台加载中...');
|
|
383
391
|
|
|
384
|
-
//
|
|
392
|
+
// 剩余重资源(模型位置、GLTF、图层树接口、屏幕图标)后台异步加载,不阻塞地图显示
|
|
385
393
|
this._loadRestAsync();
|
|
386
394
|
}
|
|
387
395
|
|
|
388
|
-
/**
|
|
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: '
|
|
395
|
-
{ nodeName: '
|
|
396
|
-
{ nodeName: '
|
|
397
|
-
{ nodeName: '
|
|
398
|
-
{ nodeName: '
|
|
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]
|
|
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]
|
|
418
|
+
console.warn(`[GaeaApp] 调整模型 ${model.nodeName} 失败:`, e.message);
|
|
411
419
|
}
|
|
412
420
|
}
|
|
413
|
-
console.log('[GaeaApp]
|
|
421
|
+
console.log('[GaeaApp] 所有模型位置调整完成');
|
|
414
422
|
|
|
415
|
-
// 6.
|
|
423
|
+
// 6. 加载 GLTF 模型
|
|
416
424
|
await this._loadGltfModels();
|
|
417
425
|
|
|
418
|
-
// 7.
|
|
426
|
+
// 7. 获取图层树数据(有接口走接口,没接口用默认)
|
|
419
427
|
await this._fetchLayerTreeData();
|
|
420
428
|
|
|
421
|
-
//
|
|
429
|
+
// 同步 layerCategories(修复数据更新未同步的问题)
|
|
422
430
|
this.layerCategories = this.config.layerCategories || [];
|
|
423
|
-
console.log('[GaeaApp] layerCategories
|
|
431
|
+
console.log('[GaeaApp] layerCategories 更新后:', this.layerCategories.length);
|
|
424
432
|
|
|
425
|
-
// 8.
|
|
433
|
+
// 8. 初始化屏幕图标
|
|
434
|
+
this._initScreenIcons();
|
|
426
435
|
|
|
427
|
-
//
|
|
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]
|
|
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('
|
|
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
|
|
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(
|
|
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
|
-
//
|
|
518
|
+
// 在线模式:仅加载天地图影像,不加载地形/贴地线/3D Tiles
|
|
509
519
|
this.addTianditu();
|
|
510
520
|
} else {
|
|
511
|
-
//
|
|
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
|
-
//
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
587
|
+
// 解锁视角(内部会调 UseAutoVisible 恢复非断面图标)
|
|
574
588
|
this.lockedModesSwitch(false);
|
|
575
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
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
|
|
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
|
|
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
|
|
632
|
-
{ name: '
|
|
633
|
-
{ name: '
|
|
634
|
-
{ name: '
|
|
635
|
-
{ name: 'YPDP0+600
|
|
636
|
-
{ name: 'YPDP0+750
|
|
637
|
-
{ name: 'YPDP0+100
|
|
638
|
-
{ name: 'YPDP0+200
|
|
639
|
-
{ name: '
|
|
640
|
-
{ name: '
|
|
641
|
-
{ name: '
|
|
642
|
-
{ name: '
|
|
643
|
-
{ name: '
|
|
644
|
-
{ name: '
|
|
645
|
-
{ name: '
|
|
646
|
-
{ name: '
|
|
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]
|
|
656
|
-
//
|
|
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]
|
|
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(
|
|
692
|
+
console.log(`模型开始加载: ${name}`);
|
|
676
693
|
|
|
677
694
|
element.OnModelInit(async () => {
|
|
678
|
-
//
|
|
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(
|
|
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]
|
|
732
|
+
console.log('[GaeaApp] 未配置 dataHubApiUrl,使用默认图层树数据');
|
|
716
733
|
return;
|
|
717
734
|
}
|
|
718
|
-
console.log('[GaeaApp]
|
|
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]
|
|
743
|
+
console.warn('[GaeaApp] 接口获取失败,使用默认数据:', e.message);
|
|
727
744
|
}
|
|
728
745
|
}
|
|
729
746
|
|
|
730
|
-
/** DataHub API
|
|
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
|
-
//
|
|
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
|
-
/**
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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]
|
|
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]
|
|
915
|
+
console.warn(`[GaeaApp] 图标 ${child.text} 创建失败:`, e.message);
|
|
898
916
|
}
|
|
899
917
|
}
|
|
900
|
-
//
|
|
918
|
+
// 根据配置的 visible 决定初始显隐(不影响创建,只控制初始状态)
|
|
901
919
|
if (cat.visible === false) {
|
|
902
920
|
this.setCategoryVisible(cat.key, false);
|
|
903
921
|
}
|
|
904
922
|
}
|
|
905
|
-
console.log('[GaeaApp]
|
|
906
|
-
//
|
|
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
|
-
*
|
|
913
|
-
* @param {
|
|
931
|
+
* 设置某个分类图标的可见性
|
|
932
|
+
* @param {string} categoryKey 分类 key(如 'seawall', 'hydrometricStations' 等)
|
|
933
|
+
* @param {boolean} visible 是否可见
|
|
914
934
|
*/
|
|
915
935
|
setCategoryVisible(categoryKey, visible) {
|
|
916
|
-
//
|
|
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, '
|
|
943
|
+
console.log('[GaeaApp] setCategoryVisible:', categoryKey, visible, '元素数:', els.length);
|
|
923
944
|
els.forEach(el => { el.style.display = visible && this._markersVisible ? '' : 'none'; });
|
|
924
|
-
//
|
|
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
|
-
*
|
|
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
|
|
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
|
|
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('
|
|
1005
|
+
if (mimeIdx === -1) return console.warn('未知图层类型:', forMat);
|
|
983
1006
|
|
|
984
|
-
if (!this.layers?.length) return console.warn('
|
|
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(
|
|
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(
|
|
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
|
-
/**
|
|
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(
|
|
1045
|
+
console.log(`地形 "${t.name}" 加载成功`);
|
|
1023
1046
|
} catch (e) {
|
|
1024
|
-
console.warn(
|
|
1047
|
+
console.warn(`地形 "${t.name}" 加载失败:`, e?.message);
|
|
1025
1048
|
}
|
|
1026
1049
|
}
|
|
1027
1050
|
}
|
|
1028
1051
|
|
|
1029
|
-
// ----
|
|
1052
|
+
// ---- 贴地线(矢量线服务) ----
|
|
1030
1053
|
|
|
1031
|
-
/**
|
|
1054
|
+
/** 从 WMTS 服务列表创建矢量要素服务 */
|
|
1032
1055
|
CreateFeature(serviceName, levelRange, render, alias) {
|
|
1033
|
-
if (!this.layers?.length) return console.warn('
|
|
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(
|
|
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
|
-
/**
|
|
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('
|
|
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(
|
|
1091
|
+
console.log(`贴地线 "${item.name}" 加载成功`);
|
|
1069
1092
|
} catch (e) {
|
|
1070
|
-
console.warn(
|
|
1093
|
+
console.warn(`贴地线 "${item.name}" 加载失败:`, e?.message);
|
|
1071
1094
|
}
|
|
1072
1095
|
}
|
|
1073
1096
|
}
|
|
1074
1097
|
|
|
1075
|
-
// ----
|
|
1098
|
+
// ---- 贴地面(矢量面绘制) ----
|
|
1076
1099
|
|
|
1077
1100
|
/**
|
|
1078
|
-
*
|
|
1079
|
-
* @param {
|
|
1080
|
-
* @
|
|
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(
|
|
1109
|
+
console.warn(`坐标点不足,跳过: ${name}`);
|
|
1085
1110
|
return null;
|
|
1086
1111
|
}
|
|
1087
|
-
//
|
|
1088
|
-
|
|
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('
|
|
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('
|
|
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
|
-
*
|
|
1163
|
-
* @param {string}
|
|
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]
|
|
1205
|
+
console.warn(`[GaeaApp] 未找到海塘: ${name}`)
|
|
1179
1206
|
};
|
|
1180
1207
|
|
|
1181
1208
|
/**
|
|
1182
|
-
*
|
|
1183
|
-
* @param {Object} item - { jsonUrl: tileset.json
|
|
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
|
|
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
|
-
*
|
|
1218
|
-
* @param {number}
|
|
1219
|
-
* @param {
|
|
1220
|
-
* @
|
|
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('
|
|
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
|
-
//
|
|
1264
|
+
// 经纬度 → 笛卡尔坐标
|
|
1265
|
+
const originPoint = pointList.map(p => Gaea.GaeaMath.SphericalToCartesianDeg(new Gaea.Vector3(p.x, p.y, p.z)))
|
|
1237
1266
|
|
|
1238
|
-
//
|
|
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
|
-
//
|
|
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]
|
|
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
|
-
*
|
|
1315
|
+
* 监听画布鼠标点击,获取点击位置的经纬度坐标
|
|
1316
|
+
* @example
|
|
1287
1317
|
* app.addMouseClick((pos) => {
|
|
1288
|
-
* console.log('
|
|
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]
|
|
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
|
|
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
|
-
*
|
|
1335
|
-
* @param {
|
|
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]
|
|
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]
|
|
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]
|
|
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
|
-
*
|
|
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]
|
|
1403
|
+
if (!el) { console.warn(`[GaeaApp] 模型 "${name}" 未找到`); return }
|
|
1372
1404
|
el.Root.Visible = visible
|
|
1373
1405
|
};
|
|
1374
1406
|
|
|
1375
1407
|
/**
|
|
1376
|
-
*
|
|
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]
|
|
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
|
-
*
|
|
1388
|
-
* @param {
|
|
1389
|
-
* @param {
|
|
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]
|
|
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
|
-
*
|
|
1409
|
-
*
|
|
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
|
-
*
|
|
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 //
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
1555
|
+
* 加载用户自定义模型(参考 ht_3dview addGltf)
|
|
1556
|
+
* 新增模型到场景并记录到配置表
|
|
1512
1557
|
* @param {object} cfg
|
|
1513
|
-
* @param {string} cfg.name -
|
|
1514
|
-
* @param {
|
|
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:
|
|
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
|
-
*
|
|
1541
|
-
* @param {number}
|
|
1542
|
-
* @param {number}
|
|
1543
|
-
* @param {number} [
|
|
1544
|
-
* @param {number} [
|
|
1545
|
-
* @param {number} [
|
|
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
|
-
*
|
|
1554
|
-
* @param {
|
|
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
|
|
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)
|
|
1683
|
+
// Godot AnimationPlayer.Stop(reset:bool),参数是布尔而非动画名
|
|
1684
|
+
if (this._flyAnimationPlayer) this._flyAnimationPlayer.Stop(false);
|
|
1634
1685
|
} catch (error) {
|
|
1635
|
-
console.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('
|
|
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
|
-
*
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
1789
|
+
// 在 DOM 上绑点击事件(Gaea ScreenGUIElement 的 OnHitElement 对 DOM 型图标不生效)
|
|
1790
|
+
container.addEventListener('click', (e) => {
|
|
1737
1791
|
e.stopPropagation();
|
|
1738
1792
|
e.preventDefault();
|
|
1739
|
-
console.log('[GaeaApp]
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
1856
|
+
/** 记录当前视角,返回 URI 字符串 */
|
|
1802
1857
|
favorite() {
|
|
1803
1858
|
return Gaea.GaeaCamera.ConvertBy(Gaea.World.Instance.DefaultCamera).Favorite();
|
|
1804
1859
|
}
|
|
1805
1860
|
|
|
1806
|
-
/**
|
|
1861
|
+
/** 定位到已记录的视角 URI */
|
|
1807
1862
|
favoritePosition(uri) {
|
|
1808
1863
|
Gaea.World.Instance.DefaultCamera.GotoFavorite(uri);
|
|
1809
1864
|
}
|
|
1810
1865
|
|
|
1811
1866
|
/**
|
|
1812
|
-
*
|
|
1867
|
+
* 添加点位标记到 3D 地图
|
|
1813
1868
|
* @param {Object} opts
|
|
1814
|
-
* @param {string} opts.name -
|
|
1815
|
-
* @param {
|
|
1816
|
-
* @param {number} opts.
|
|
1817
|
-
* @param {number}
|
|
1818
|
-
* @param {
|
|
1819
|
-
* @param {
|
|
1820
|
-
* @param {
|
|
1821
|
-
* @param {
|
|
1822
|
-
* @param {
|
|
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
|
-
//
|
|
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"
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
2001
|
+
// 记录已升起的断面模型,供重置时缩回
|
|
2002
|
+
this._raisedSection = sectionModel;
|
|
1943
2003
|
if (tourModel) {
|
|
1944
2004
|
this.lockedModesSwitch(true, tourModel, [-10, 50, 0], [50, 50]);
|
|
1945
|
-
//
|
|
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
|
|
2024
|
+
cls = 'waterLevel'; offset = [0, -54]; imgUrl = '/image/labelImg/水位.png';
|
|
1964
2025
|
} else {
|
|
1965
|
-
cls = 'damScreen'; offset = [0, -19]; imgUrl = '/image/labelImg
|
|
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]
|
|
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('
|
|
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('
|
|
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(
|
|
2117
|
+
reject(new Error(`动画执行出错:${error.message}`));
|
|
2057
2118
|
}
|
|
2058
2119
|
});
|
|
2059
2120
|
};
|
|
2060
2121
|
|
|
2061
|
-
/**
|
|
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
|
-
//
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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]
|
|
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:
|
|
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
|
-
/**
|
|
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
|
|
2303
|
+
console.error('addPointIcon: lat 和 lng 不能为空');
|
|
2240
2304
|
return null;
|
|
2241
2305
|
}
|
|
2242
|
-
//
|
|
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:
|
|
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
|
-
/**
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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:
|
|
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]] -
|
|
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); //
|
|
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
|
-
*
|
|
2428
|
-
* @param {Object}
|
|
2429
|
-
* @param {Object}
|
|
2430
|
-
* @param {Object}
|
|
2431
|
-
* @param {
|
|
2432
|
-
* @param {number}
|
|
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
|
-
*
|
|
2496
|
-
* @param {
|
|
2497
|
-
* @param {string} opts.
|
|
2498
|
-
* @param {
|
|
2499
|
-
* @param {
|
|
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)
|