# @yangyongtao/gaea

基于 Godot WASM 引擎的 Vue 3 三维可视化组件库。提供影像/地形/贴地线加载、相机视角控制、图层树、天气控制、沿线飞行、断面游览、室内游览等能力，并内置 Vite 插件自动注入引擎资源。

> 完整可运行示例参考消费者项目的 `src/App.vue`，本文所有片段均以其为参考。

---

## 1. 安装

```bash
npm install @yangyongtao/gaea
```

peerDependencies（按需安装）：`vue ^3.4`、`element-plus ^2.6`（可选）、`pinia ^2.1`（可选）。

> ⚠️ 引擎大资源文件（`Gaea.pck` / `Gaea.wasm` / `1111.res` / `gltf` 等）**不随 npm 包分发**，需手动放置，详见第 8 节。

---

## 2. 引入方式

本库同时支持 ESM 和 CommonJS。

### ESM（推荐，vue-cli / vite / webpack 默认）

```js
import { GaeaApp, setConfig } from '@yangyongtao/gaea';
import { WeatherControl, FlyAlongRoute } from '@yangyongtao/gaea/components';
import { gaeaVitePlugin } from '@yangyongtao/gaea/plugin';
```

### CommonJS

```js
// 需要使用 await import() 或 .then()
const gaea = await import('@yangyongtao/gaea');
gaea.setConfig({ ... });

// vite 配置中使用 CJS 引用插件
const { gaeaVitePlugin } = await import('@yangyongtao/gaea/plugin');
```

> CJS 环境下 **不能** 直接用 `require()`，因为内部实现是 ESM 模块。Node.js 对 CJS `require` ESM 有限制，因此 CJS 包装入口返回的是 Promise。

---

## 3. Vite 配置

引入并注册 `gaeaVitePlugin`，它会自动注入 `Gaea.js`、提供引擎静态资源、设置 SharedArrayBuffer 所需的跨域隔离响应头。

```js
// vite.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { gaeaVitePlugin } from '@yangyongtao/gaea/plugin';

export default defineConfig({
  plugins: [vue(), gaeaVitePlugin()],
  resolve: { preserveSymlinks: true },
  optimizeDeps: { exclude: ['@yangyongtao/gaea'] },
});
```

---

## 4. 快速开始

页面里需要一个 `id="canvas"` 的画布。调用 `setConfig(...)` 后 `new GaeaApp()` 即可自动完成「引擎启动 → 环境初始化 → 图层加载 → 模型加载」。

```vue
<template>
  <div id="gaea-app">
    <div class="toolbar">
      <button @click="showWeather = !showWeather">天气控制</button>
      <button @click="showFly = !showFly">沿线飞行</button>
      <button @click="showLayerTree = !showLayerTree">图层控制</button>
      <WaterGateTour />
    </div>

    <div class="canvas-area" id="canvas-container">
      <canvas id="canvas"></canvas>
    </div>

    <WeatherControl v-if="showWeather" :appInstance="app" @close="showWeather = false" />
    <FlyAlongRoute v-if="showFly" :appInstance="app" @close="showFly = false" />
    <LayerTree v-if="showLayerTree" :appInstance="app" @close="showLayerTree = false" @node-click="onLayerNodeClick" />
    <HydrologicStationDialog />
    <SecTourView />
  </div>
</template>

<script setup>
import { ref } from 'vue';
import { GaeaApp, setConfig } from '@yangyongtao/gaea';
import { WeatherControl, FlyAlongRoute, HydrologicStationDialog, SecTourView, WaterGateTour } from '@yangyongtao/gaea/components';
import LayerTree from '@yangyongtao/gaea/components/LayerTree.vue';

setConfig({
  serveLocal: `${window.location.origin}/static`,
  serveUrl: 'http://192.168.3.151:8088/gaeaExplorerServer/',
  serviceMode: 'platform',
  defaultCameraPosition: '27.3680928977018,120.414139621887,0,109379.690132486,0,0',
});

const app = new GaeaApp();
window.APP = app;               // 组件内部通过 window.APP 兜底获取实例

const showWeather = ref(false);
const showFly = ref(false);
const showLayerTree = ref(false);

function onLayerNodeClick({ node }) {
  app.flyToNode(node);
}
</script>
```

---

## 5. setConfig 配置项

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `serveLocal` | string | `''` | 引擎本地静态资源地址（AssetHost），一般为 `${origin}/static` |
| `serveUrl` | string | `''` | 服务平台地址（WMTS 影像 / 地形 / 贴地线均基于此） |
| `serviceMode` | `'platform' \| 'online'` | `'platform'` | **服务模式**：见下方说明 |
| `tiandituToken` | string | — | 在线模式的天地图 token |
| `tiandituLevelRange` | [number,number] | `[0,18]` | 天地图层级范围 |
| `defaultLayers` | Array | 见下 | 服务平台模式下加载的影像图层 |
| `defaultTerrains` | Array | 见下 | 服务平台模式下加载的地形服务 |
| `defaultGroundLines` | Array | 见下 | 服务平台模式下加载的贴地线 |
| `defaultCameraPosition` | string | — | 初始相机视角：`纬度,经度,高程,相机高,俯仰,朝向` |
| `layerCategories` | Array | 内置 | 图层树分类数据（接口数据会合并进来） |
| `dataHubApiUrl` | string | `''` | 图层树数据接口地址，为空则只用默认数据 |
| `onProgress` | function | — | 引擎加载进度回调 `(cur, total) => {}` |

### 5.1 serviceMode —— 服务模式（区分「服务平台」与「在线」）

- `'platform'`（服务平台）：影像走 `serveUrl` 的 WMTS，并加载 `defaultTerrains` 地形、`defaultGroundLines` 贴地线。
- `'online'`（在线）：**仅加载天地图影像**，不加载地形和贴地线（在线模式目前只有天地图一种影像来源）。

```js
setConfig({ serviceMode: 'platform' });  // 内网/服务平台
// 或
setConfig({ serviceMode: 'online', tiandituToken: '你的token' }); // 在线（天地图）
```

### 5.2 defaultLayers —— 影像图层（服务平台模式）

```js
defaultLayers: [
  { layerName: 'earthland', forMat: 'image/png', min: 0, max: 18, transparency: 0.3 },
  { layerName: '苍南L19',   forMat: 'image/png', min: 5, max: 17 },
]
```
- `layerName`：WMTS 服务里的图层名（Title）
- `forMat`：影像格式，如 `image/png`、`image/jpeg`
- `min` / `max`：显示层级范围
- `transparency`：透明度（可选）

### 5.3 defaultTerrains —— 地形服务（服务平台模式）

```js
defaultTerrains: [
  { name: '苍南县地形', url: 'htc/service/tms/1.0.0/苍南县地形@EPSG%3A4326@terrain/' },
]
```
- `name`：地形名称
- `url`：相对 `serveUrl` 的路径（也支持传完整 `http(s)://` 地址）
- 空数组 `[]` 则不加载地形

### 5.4 defaultGroundLines —— 贴地线（服务平台模式）

依赖 `serveUrl` 的 WMTS 矢量线服务（自动通过 `getAllServe` 获取服务列表后按名匹配）。

```js
defaultGroundLines: [
  { name: '苍南县边界',        range: [0, 9],   color: [255, 255, 0],   index: 100 },
  { name: '苍南县镇边界',      range: [9, 15],  color: [0, 255, 0],     index: 80 },
  { name: '苍南县村边界',      range: [15, 19], color: [255, 255, 255], index: 60 },
  { name: '沿浦海塘工程范围线', range: [0, 21],  color: [0, 255, 255],   index: 120 },
]
```
- `name`：矢量线服务名（Title）
- `range`：显示层级范围 `[min, max]`
- `color`：线颜色 `[r, g, b]`（0-255）
- `index`：渲染优先级
- 每项可加 `isInitLoad: false` 跳过该条

---

## 6. 组件说明

组件从 `@yangyongtao/gaea/components` 导入；`LayerTree` 需按路径单独导入。多数交互组件接收 `:appInstance="app"`，未传时内部回退到 `window.APP`。

| 组件 | 说明 | 主要事件 |
|------|------|----------|
| `WeatherControl` | 天气/台风控制面板（含台风路线拾取与播放） | `@close` |
| `FlyAlongRoute` | 沿线飞行：地图拾取点位，可设飞行高度/时长 | `@close` |
| `LayerTree` | 图层树，点击节点飞行定位 | `@close`、`@node-click` |
| `HydrologicStationDialog` | 水文测站弹窗，自监听 `gaea-icon-click` 事件 | — |
| `SecTourView` | 断面游览滚动条，模型锁定后显示 | — |
| `WaterGateTour` | 室内游览按钮（下拉选择水闸模型） | — |

---

## 6. 常用实例方法（window.APP / app）

| 方法 | 说明 |
|------|------|
| `SetPosition('纬度,经度,高程,相机高,俯仰,朝向')` | 定位到指定视角 |
| `flyToNode({ position:{x,y,z} })` | 飞到指定位置 |
| `flyAlongPath(lineData, options)` | 沿线飞行，`lineData=[{x:纬度,y:经度,z:高度}]`，`options={duration,xrotation,lockEye,...}` |
| `stopFlyAlongPath()` | 停止沿线飞行 |
| `cameraReset()` | 相机复位到 `defaultCameraPosition` |
| `addInitTerrainServer(terrains?)` | 手动加载地形（不传则用 `defaultTerrains`） |
| `addGroundLines(lines?)` | 手动加载贴地线（不传则用 `defaultGroundLines`） |
| `addTyphoonRoute(pointList, options)` | 加载台风路线并播放动画 |
| `clearTyphoonRoute()` | 清除台风路线 |
| `getAllServe()` | 获取 WMTS 服务图层列表 |
| `addLayer({ layerName, forMat, min, max, transparency })` | 添加影像图层 |
| `favorite()` / `favoritePosition(uri)` | 记录 / 恢复视角 |

---

## 7. 注意事项

- 页面必须存在 `id="canvas"` 的 `<canvas>`，引擎会挂载到它上面。
- 需要跨域隔离（COOP/COEP）才能启用 SharedArrayBuffer，`gaeaVitePlugin` 已自动处理；首次进入若未隔离会自动刷新一次。
- 服务平台模式下地形/贴地线依赖 `serveUrl` 对应服务，若服务器无对应服务名，控制台会打印「未找到/加载失败」警告，不影响其它功能。
- 大体积引擎资源不随 npm 包分发，需手动放置，详见第 8 节。

---

## 9. 大资源文件的放置

引擎运行需要的大文件体积过大，**不包含在 npm 包内，也不会自动下载**（`postinstall` 仅做检测与提示）。请通过内网传输或手动拷贝的方式，将它们放到消费者项目里 gaea 包的 `public` 目录下。

### 9.1 需要放置的文件

放置目标目录：`node_modules/@yangyongtao/gaea/public/`

| 文件 / 目录 | 目标位置 | 参考大小 | 说明 |
|------|------|------|------|
| `Gaea.pck` | `public/Gaea.pck` | ~450 MB | 引擎主资源包 |
| `Gaea.wasm` | `public/Gaea.wasm` | ~16 MB | 引擎 WASM |
| `1111.res` | `public/static/1111.res` | **~34.6 MB** | 点图标/纹理资源包（务必用完整版，见下方警告） |
| `gltf/` | `public/static/gltf/` | 若干 | 断面/水闸等 GLTF 模型 |

> ⚠️ **重要**：`1111.res` 必须使用 **完整版（约 34.6 MB）**。早期存在一个残缺的 6.5MB 版本，会导致点图标纹理为空、加载台风路线时引擎崩溃（`SimpleFeatureRenderer.CombineSymbolMeshs` 空引用）。放置后可用文件大小快速校验。

### 9.2 校验是否就位

```powershell
# Windows PowerShell
Get-Item node_modules/@yangyongtao/gaea/public/Gaea.pck | Select-Object Length
Get-Item node_modules/@yangyongtao/gaea/public/static/1111.res | Select-Object Length
```
`Gaea.pck` 约 450MB、`1111.res` 约 34.6MB 即为正确。

### 9.3 注意事项

- 更换 `1111.res` 后，若浏览器曾加载过旧的残缺资源，引擎会把资源按名缓存进持久化虚拟文件系统，需**强制刷新（Ctrl+Shift+R）**一次；本库已用版本化资源名规避该缓存问题。
- 若不放置这些文件，`npm install` 后控制台会打印提示，且页面无法正常渲染三维场景。
- CI 等无需下载/提示的环境，可设置环境变量 `GAEA_SKIP_DOWNLOAD=1` 跳过 postinstall 提示。

---

## License

MIT
