# 坐标系 (Coordinate System)

- **Summary**: 详解 Five 中的坐标系定义（Local, ENU, ECEF）及其相互转换关系。
- **Concepts**: Local (Y-up), ENU (Z-up), ECEF, ENU84, RootMeta, Observers.
- **Configuration**: `rootMeta` `coordinate` 配置。
- **Examples**: 坐标转换方法示例。

## Schema

> **Definition**: [TilesetHeader](../../five/model/tile3d/header.d.ts)

### RootMeta

3D Tiles `rootMeta` 字段中定义的坐标系元数据。

```typescript
interface RootMeta {
  /** 模型垂直轴向 (如 "Z", "-Y") */
  upAxis?: string;
  /** 坐标系转换矩阵信息 */
  coordinate?: Coordinate;
}
```

### Coordinate

描述 ECEF 与 ENU 之间的转换关系。

```typescript
interface Coordinate {
  /** 地面高度 */
  ground_height?: number;
  /** ECEF 到 ENU 的转换矩阵 (4x4 数组) */
  pose_ecef_to_enu?: number[];
  /** ENU 到 ECEF 的转换矩阵 (4x4 数组) */
  pose_enu_to_ecef?: number[];
}
```

## Concepts

### Overview (坐标系总览)

Five 中涉及多个坐标系，它们在不同场景下各有用途。理解它们的关系是正确使用 Five 的关键。

| 坐标系 | 上轴 | 适用场景 | 说明 |
| :--- | :--- | :--- | :--- |
| **Local (Y-up)** | Y | 渲染场景、前端开发 | Five 的 `five.scene`、Observer 位置、`project2d` 等均使用此坐标系。与 Three.js 默认一致。 |
| **ENU (Z-up)** | Z | 算法交互、后端接口、模型原始数据 | 测绘行业习惯。3D Tiles 数据源、`loadGltf` 等加载器默认输出 Z-up。 |
| **ECEF** | - | RTK 定位、地球坐标 | 地心地固坐标系，用于包含 RTK 信息的模型。 |
| **World** | Y | Three.js 世界坐标 | 通常等同于 Local，除非有 Object3D 层级嵌套偏移。 |

> **关键区分**：
> - **开发者在 `five.scene` 中操作的所有对象**（添加标签、射线检测结果、`project2d` 输入等）都处于 **Local (Y-up)** 坐标系。
> - **模型数据源**（3D Tiles、PLY、GLTF 等文件）通常以 **ENU (Z-up)** 存储。Five 在加载时会自动将其旋转对齐到 Local (Y-up)。
> - **与后端/算法交互**时，推荐使用 **ENU (Z-up)** 坐标系传输数据，通过 `viewLayer.localToEnu()` / `viewLayer.enuToLocal()` 进行转换。

### Local Coordinate System (Y-up)
Five 的渲染场景 (`five.scene`) 和 Observers (观察点) 使用 **Y-up** (Y 轴向上) 的右手坐标系。
这与 Three.js 的默认坐标系一致，便于前端开发和场景渲染。
- **Y 轴**: 垂直向上。
- **X/Z 轴**: 水平平面。

### ENU Coordinate System (Z-up)
在本项目及相关算法交互中，**ENU** 指代 **Z-up** (Z 轴向上) 的坐标系。这是为了和测绘行业习惯保持一致。
- **Z 轴**: 垂直向上。
- **X/Y 轴**: 水平平面。
- **ENU84**: 特指 Y 轴正向指向正北 (North) 的 ENU 坐标系。（注：普通的 ENU 坐标系中，北向不一定对应 Y 轴，这与部分行业标准有所区别）。
- **用途**: 推荐在给算法或后端接口传递模型数据时，使用 ENU 坐标系。

### ECEF Coordinate System
地心地固坐标系 (Earth-Centered, Earth-Fixed)。
- 当模型包含 RTK (Real-time kinematic) 信息时，通常基于 ECEF 坐标系进行描述。
- 可以通过 `ecefToEnu` 方法将 ECEF 坐标转换为 ENU 坐标。

### Model Orientation (at3d / 3dtiles)
模型 (如 `.at3d`, 3D Tiles) 的加载逻辑遵循以下原则：
1.  **无 RTK 信息**: 默认为 **Z-up** (ENU)。
2.  **有 RTK 信息**: 遵循 **ECEF** 坐标系。
3.  **自动对齐**: Five 加载模型时（load 方法），会自动处理旋转和坐标系转换，将其对齐到 Five 的 Local (Y-up) 坐标系中。
    - 开发者在 `five.scene` 中看到的所有模型都已处于统一的 Y-up 坐标系下。

> **注意**: 外部模型加载函数（如 `loadGltf`, `loadPly` 等）的 `upAxis` 参数默认为 `'Z'`，表示输出的模型以 Z-up 存储。这是因为这些函数返回的是**原始模型对象**，尚未经过 Five 的自动对齐。当你通过 `five.load(work)` 加载 Work 数据时，Five 会自动完成 Z-up → Y-up 的转换。如果你手动使用 `loadGltf` 并添加到 `five.scene`，需要自行处理坐标系旋转。详见 [Load External Model](./load-external-model.md)。

### Coordinate in Files
在 3D Tiles 的 `tileset.json` 中，`rootMeta` 字段用于描述坐标系转换信息（没有该字段则默认 Z-up）：
- 包含 `pose_ecef_to_enu` 和 `pose_enu_to_ecef` 变换矩阵。
- Five 读取这些信息来计算并构建完整的坐标转换链, 进行模型-观察点的坐标对齐。

在 model 的 `coordinate` 字段用于描述坐标系转换信息（没有该字段则默认 Z-up）：
- 包含 `pose_ecef_to_enu` 和 `pose_enu_to_ecef` 变换矩阵。
- Five 读取这些信息来计算并构建完整的坐标转换链，确保模型-观察点的坐标对齐。

## Instance Methods

`ViewLayer` (继承自 `Tile3D`) 提供了成套的坐标转换方法，用于在 Local, ENU, ECEF 和 World 之间进行转换。

| 方法名 | 说明 |
| --- | --- |
| `localToEnu(vector)` | Local (Y-up) -> ENU (Z-up) |
| `enuToLocal(vector)` | ENU (Z-up) -> Local (Y-up) |
| `enuToEcef(vector)` | ENU (Z-up) -> ECEF |
| `ecefToEnu(vector)` | ECEF -> ENU (Z-up) |
| `localToEcef(vector)` | Local (Y-up) -> ECEF |
| `ecefToLocal(vector)` | ECEF -> Local (Y-up) |
| `worldToEnu(vector)` | World -> ENU (Z-up) |
| `enuToWorld(vector)` | ENU (Z-up) -> World |

> 注: `World` 坐标系通常指 Three.js 的世界坐标 (即 `five.scene` 的坐标)。在未通过 `Object3D` 层级嵌套偏移的情况下，`World` 等同于 `Local`。

## Examples

### 坐标转换 (Coordinate Transformation)

推荐在与后端或算法交互时，将 Local 坐标转换为 ENU 坐标。

```typescript
import * as THREE from 'three';
import { ViewLayer } from '@realsee/five';

// 假设已加载 ViewLayer
const viewLayer: ViewLayer = ...;

// 1. 获取场景中某点的坐标 (Local Y-up)
const localPosition = new THREE.Vector3(10, 20, 30);

// 2. 转换为 ENU 坐标 (Z-up) 用于传输给后端/算法
const enuPosition = viewLayer.localToEnu(localPosition.clone());

console.log(`Local: ${localPosition.toArray()}`); // [x, y, z] (y-up)
console.log(`ENU: ${enuPosition.toArray()}`);     // [x, y, z] (z-up)

// 3. 将 ECEF 坐标转换为 Local 坐标用于显示
const ecefPosition = new THREE.Vector3( ... );
const displayPosition = viewLayer.ecefToLocal(ecefPosition.clone());
```

## Related

*   [Load External Model](./load-external-model.md): 加载外部模型时的坐标轴配置 (`upAxis`, `modelUpAxis`)。

---

```yaml
tags: [坐标系, 坐标转换, 右手坐标系, 模型方向, 旋转, 地理坐标, coordinate-system, enu, ecef, z-up, y-up, transform, local, world, rtk]
```
