# 坐标系 (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

### RootMeta

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

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

### Coordinate

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

    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。

### 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 坐标。

    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: 加载外部模型时的坐标轴配置 (`upAxis`, `modelUpAxis`)。

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