# 操作指南：从 CAD 图纸生成 airport_roads.yaml 配置文件

## 1. 目标与挑战

本指南旨在说明如何使用免费开源的 GIS 软件 QGIS，从机场的 CAD 工程图纸（通常使用局部坐标系）生成包含精确地理坐标（经纬度，WGS84）的 `airport_roads.yaml` 配置文件，以供应用程序使用。

主要挑战在于将 CAD 的局部坐标系转换为标准的 WGS84 地理坐标系，这需要准确的地理参考信息。

## 2. 准备工作

在开始之前，请确保你拥有：

*   **QGIS 软件**: 从 [QGIS 官网](https://qgis.org/) 下载并安装最新稳定版。
*   **机场 CAD 图纸**: `.dwg` 或 `.dxf` 格式的文件。最好是包含清晰道路中心线的版本。
*   **地面控制点 (GCPs - Ground Control Points)**: **这是最关键的部分！** 你需要获取 CAD 图上至少 3-4 个（越多越好，分布越均匀越好）易于识别的点（如建筑角点、跑道端点、特定标记）的**精确真实世界地理坐标**。坐标可以是：
    *   **经纬度 (WGS84)**: 例如 `[经度 113.12345, 纬度 22.54321]`
    *   **投影坐标**: 例如 UTM 坐标，并清楚知道其对应的区域和基准面。
    *   获取方式：可能来自现场 GPS 测量、官方测绘数据、高精度卫星地图的比对等。**控制点的精度直接决定了最终结果的精度。**

## 3. QGIS 安装

访问 [QGIS 官网](https://qgis.org/)，根据你的操作系统下载并安装 QGIS Desktop。

## 4. CAD 文件准备（可选但推荐）

为了简化后续操作，建议在 CAD 软件中：

*   清理图纸，只保留必要的图层，尤其是**道路中心线**图层和用于**地理配准的参考点**所在图层。
*   确保道路中心线是连接良好的线段 (Polyline)。
*   如果可能，将文件另存为较旧版本的 `.dxf` 格式，有时兼容性更好。

## 5. 导入 CAD 文件到 QGIS

1.  打开 QGIS。
2.  通过菜单 `图层 (Layer)` -> `添加图层 (Add Layer)` -> `添加矢量图层 (Add Vector Layer...)` 打开数据源管理器。
3.  在 `矢量 (Vector)` 选项卡中，选择 `文件 (File)` 类型。
4.  点击 `源 (Source)` 旁边的 `...` 按钮，浏览并选择你的 `.dwg` 或 `.dxf` 文件。
5.  点击 `添加 (Add)`。QGIS 可能会询问要导入哪些图层（如果 CAD 文件包含多个图层），选择包含道路中心线和参考点的图层。
6.  关闭数据源管理器。你现在应该能在 QGIS 地图中看到 CAD 图纸的内容。此时，它的坐标系还是未知的或局部的。

## 6. 地理配准 (Georeferencing)

这是将 CAD 局部坐标转换为地理坐标的关键步骤。

1.  **打开地理配准器**: 通过菜单 `图层 (Layer)` -> `地理配准器 (Georeferencer...)` 打开工具。
2.  **准备并加载要配准的图像**:
    *   由于地理配准器主要处理栅格图像，你需要先将导入的 CAD 图层视图导出为图像文件。
    *   在 QGIS 主窗口，调整视图以清晰显示 CAD 图纸内容和参考点。
    *   通过菜单 `项目 (Project)` -> `导入/导出 (Import/Export)` -> `导出地图为图像 (Export Map to Image)...`。
    *   设置合适的范围（例如 `地图画布范围` 或 `计算自图层` -> 选择 CAD 图层），并确保设置一个**足够高的分辨率**（例如 300 DPI 或更高）以保证后续精确选取控制点。点击 `保存 (Save)`，选择文件名（如 `cad_export.tif`）和位置。
    *   **加载图像到地理配准器**: 回到地理配准器窗口，点击工具栏上的 `打开栅格 (Open Raster)` 按钮。在弹出的文件选择窗口中，找到并选择你刚刚导出的图像文件 (`cad_export.tif`)。
3.  **添加地面控制点 (GCPs)**:
    *   在地理配准器地图窗口中，找到你的第一个已知控制点（例如，某个建筑的角点）。
    *   使用工具栏上的 `添加点 (Add Point)` 工具，在图上精确点击该点。
    *   会弹出一个 `输入地图坐标 (Enter map coordinates)` 的对话框。**不要输入** X/Y 坐标（那是源坐标，让 QGIS 自动获取），而是点击 `来自地图画布 (From map canvas)`（如果你的控制点在另一地图层可见）或**手动输入该点的真实世界坐标**（经度 Longitude 对应 X，纬度 Latitude 对应 Y）。**确保输入的是 WGS84 经纬度坐标！** 点击 `确定 (OK)`。
    *   该控制点会出现在下方的 GCP 表中。
    *   重复此过程，为所有已知的控制点添加映射关系（至少 3 个，推荐 4 个以上，分布均匀）。GCP 表中的 `dX`, `dY` 和 `残差 (Residual)` 列可以帮助判断点的精度，残差值越小越好。
4.  **设置变换参数**:
    *   点击工具栏上的 `变换设置 (Transformation settings)` 按钮（黄色齿轮图标）。
    *   **变换类型 (Transformation type)**: 根据控制点数量和分布选择。`线性 (Linear)` 适用于只有少数点或简单变换；`Helmert`能做平移、旋转等变换。 `多项式1/2/3 (Polynomial 1/2/3)` 能处理更复杂的形变，但可能会导致变形过大。`薄板样条 (Thin Plate Spline)` 适用于需要局部精确变形的情况。线性和 Helmert 都不改变地图本身形状。这里选择`Helmert`。
    *   **重采样方法 (Resampling method)**: 如果是基于栅格配准，选 `最近邻 (Nearest neighbour)`。
    *   **目标坐标系 (Target CRS)**: **极其重要！** 点击 `选择 CRS (Select CRS)` 按钮，搜索并选择 `WGS 84` (其 EPSG 代码通常是 **4326**)。
    *   **输出栅格 (Output raster)**: 指定配准后文件的保存位置和名称。建议保存为 GeoPackage (`.gpkg`) 或 GeoTIFF (`.tif`) 格式。
    *   勾选 `完成后在 QGIS 中加载 (Load in QGIS when done)`。
    *   点击 `确定 (OK)`。
5.  **执行地理配准**: 点击工具栏上的 `开始地理配准 (Start Georeferencing)` 按钮（绿色播放图标）。
6.  配准完成后，关闭地理配准器。新的、已地理配准的图层会添加到 QGIS 主窗口。你可以通过添加一个在线地图背景（如 OpenStreetMap）来验证配准效果是否准确。

## 6.5 矢量图层仿射初步变换（推荐）

如果你的CAD道路中心线图层坐标范围与地理底图（如配准后的栅格或OSM）相差极大，建议先用QGIS的"仿射变换"工具将其大致平移、缩放到目标区域：

1. 在图层面板中选中你的道路中心线图层（如`roads_centerline`）。
2. 菜单栏选择 `矢量` → `几何工具` → `仿射变换`（Affine transform）。
3. 在弹出的对话框中填写参数（以青岛机场的 CAD 图纸为例）：
   - Translation (x-axis)：`119.98`（经度方向平移，单位度）
   - Translation (y-axis)：`36.24`（纬度方向平移，单位度）
   - Scale factor (x-axis)：`0.00001`
   - Scale factor (y-axis)：`0.00001`
   - Rotation around z-axis：保持`0.0`（EPSG:4326下此参数无效）
   - 其他参数保持默认
4. 输出选择"创建临时图层"或指定保存位置。
5. 点击"运行"，生成大致对齐的新图层。

> **注意：** 仿射变换只做粗略对齐，后续还需精确配准。

## 6.6 用Vector Bender插件两对点法精确对齐

1. 安装并启用Vector Bender插件。
2. 创建一条线图层作为Pairs layer（点对图层）或直接使用 Vector Bender 的 Pairs layer 图层，并切换到编辑模式。
3. 在Pairs layer中用"添加线要素"工具，分别绘制两条线：
   - 每条线的起点为仿射变换后道路图层上的特征点（如交叉口、端点），终点为底图（如配准栅格或OSM）上对应的真实地理位置。
   - 推荐选择分布较远、方向不同的两对点。
4. 保存并退出编辑模式。
5. 打开Vector Bender插件：
   - Layer to bend 选择仿射变换后的道路图层
   - Pairs layer 选择刚才绘制的点对图层
   - 勾选"Change pairs to pins"
   - 点击"Run"
6. 插件会自动完成平移、缩放、旋转，使两对点完全重合，实现道路图层与底图的精准对齐。
7. 检查结果，确认道路几何关系和位置均正确。

> **注意：** 只用两对点即可实现无畸变的仿射对齐，几何关系不会被破坏。

## 7. 数字化道路中心线（修正版）

> **重要：** 请务必先完成仿射变换和Vector Bender两对点精确对齐，再进行属性补充、导出等后续操作。

1. 对齐后的道路中心线图层可直接用于属性补充。
2. 如需补充或修改道路，可在该图层上继续编辑。
3. 完成后，右键图层 → 导出 → 要素另存为...，选择GeoJSON等格式，确保CRS为WGS84。

## 7.1 修改字段属性

在QGIS中，可以使用"重构字段"（Refactor fields）工具来修改字段名和数据类型。

### 7.1.1 修改字段名

1. 打开处理工具箱：
   - 菜单 `处理` → `工具箱`
   - 或使用快捷键 Ctrl+Alt+T

2. 搜索并打开"重构字段"工具：
   - 在搜索框中输入"重构字段"
   - 双击打开工具对话框

3. 设置参数：
   - 在"输入图层"下拉列表中选择要修改的图层
   - 在字段映射表中：
     - 找到要修改的字段
     - 双击"名称"列，输入新的字段名
   - 在"输出文件"中设置保存位置

4. 点击"运行"执行修改

### 7.1.2 修改字段数据类型

1. 打开"重构字段"工具（步骤同上）

2. 设置参数：
   - 在"输入图层"下拉列表中选择要修改的图层
   - 在字段映射表中：
     - 找到要修改的字段
     - 双击"类型"列，选择新的数据类型
     - 常见类型包括：
       - 整数（Integer）
       - 小数（Decimal/Real）
       - 文本（String）
       - 布尔值（Boolean）
   - 在"输出文件"中设置保存位置

3. 点击"运行"执行修改

### 7.1.3 批量修改字段值

使用字段计算器可以批量修改某个字段的所有值：

1. 确保图层处于编辑模式：
   - 右键点击图层
   - 选择"切换编辑模式"（Toggle Editing）

2. 打开属性表：
   - 右键点击图层
   - 选择"打开属性表"（Open Attribute Table）

3. 使用字段计算器：
   - 点击属性表工具栏上的"字段计算器"按钮（计算器图标）
   - 在弹出的对话框中：
     - 勾选"更新现有字段"（Update existing field）
     - 在下拉列表中选择要修改的字段
     - 在表达式框中输入新值（例如：`3.80`）
     - 点击"确定"

4. 保存修改：
   - 检查属性表中的值是否已全部更新
   - 点击工具栏上的"保存图层编辑"按钮
   - 或右键图层 → "切换编辑模式" → 选择"保存"

> **注意：**
> - 如果只想修改特定记录，可以先使用选择工具选择要修改的记录
> - 修改前建议先备份数据
> - 确保新值的数据类型与字段类型兼容

## 8. 导出数字化道路为 GeoJSON

GeoJSON 是易于程序处理的格式。

1.  在 `图层 (Layers)` 面板中，右键单击 `roads_centerline` 图层。
2.  选择 `导出 (Export)` -> `要素另存为... (Save Features As...)`。
3.  **格式**: 选择 `GeoJSON`。
4.  **文件名**: 指定导出的 GeoJSON 文件名和保存位置，例如 `airport_roads.geojson`。
5.  **坐标系 (CRS)**: 确保选择的是 `WGS 84 (EPSG:4326)`。
6.  **导出字段**: 确保所有需要的属性字段都被勾选导出。
7.  **几何图形**: 可以设置坐标精度（小数位数），根据需要调整。
8.  点击 `确定 (OK)`。

## 9. 从 GeoJSON 生成 YAML

现在你有了一个包含所有道路几何和属性的 GeoJSON 文件。你需要编写一个脚本（例如 Python）来将其转换为 `airport_roads.yaml` 格式。

**脚本逻辑概要 (以 Python 为例):**

```python
import json
import yaml # 需要安装 PyYAML: pip install pyyaml

geojson_file = 'airport_roads.geojson'
yaml_file = 'src/main/resources/config/airport_roads.yaml' # 目标路径

output_data = {
    'airport_code': 'XYZ', # 或者从其他地方获取
    'roads': []
}

with open(geojson_file, 'r', encoding='utf-8') as f:
    geojson_data = json.load(f)

for feature in geojson_data['features']:
    props = feature['properties']
    coords = feature['geometry']['coordinates']

    road_entry = {
        'id': props.get('road_id'),
        'name': props.get('name'),
        'geometry': {
            'type': 'LineString',
            'coordinates': coords # GeoJSON 的坐标列表可以直接用
        },
        'width': {
            'value': props.get('width_value'),
            'unit': props.get('width_unit', 'm')
        },
        'speed_limit': {
            'value': props.get('speed_limit_value'),
            'unit': props.get('speed_limit_unit', 'km/h')
        },
        'directionality': props.get('directionality'),
        # ... 其他字段类似处理 ...
        # 注意处理 None 或缺失值，以及数据类型转换
        'prohibited': bool(props.get('prohibited', False)), # 示例：处理布尔值
        # height_limit, width_limit 需要判断值是否存在再添加
        # related_zones 可能需要特殊处理，例如如果 GeoJSON 里是逗号分隔字符串，这里要转成列表
    }

    # 添加可选字段
    if props.get('height_limit_value') is not None:
        road_entry['height_limit'] = {
            'value': props.get('height_limit_value'),
            'unit': props.get('height_limit_unit', 'm')
        }
    if props.get('width_limit_value') is not None:
        road_entry['width_limit'] = {
            'value': props.get('width_limit_value'),
            'unit': props.get('width_limit_unit', 'm')
        }
    if props.get('related_zones'):
         # 假设 related_zones 在 QGIS 中是以逗号分隔的字符串输入的
         related_zones_list = [zone.strip() for zone in props.get('related_zones').split(',')] 
         road_entry['related_zones'] = related_zones_list


    output_data['roads'].append(road_entry)

# 写入 YAML 文件
with open(yaml_file, 'w', encoding='utf-8') as f:
    yaml.dump(output_data, f, allow_unicode=True, default_flow_style=False, sort_keys=False)

print(f"YAML 文件已生成: {yaml_file}")

```

你需要根据你在 QGIS 中定义的实际字段名和数据类型来调整脚本。

## 10. 总结

通过以上步骤，你可以将 CAD 图纸中的道路信息转换为包含精确地理坐标的 YAML 配置文件。关键在于获取准确的地面控制点 (GCPs) 以及在 QGIS 中细致地完成地理配准和数字化工作。这个过程可能比较耗时，但能确保配置数据的质量。 