znlgis 博客

GIS开发与技术分享

第五章:变量系统与步骤引用

GeoPipeAgent 流水线中有两种核心数据传递机制:变量替换${var})和步骤引用$step-id)。本章深入解析这两种机制的工作原理和使用技巧。


5.1 变量替换:${var_name}

5.1.1 基本用法

pipeline.variables 中定义变量,在步骤 params 中用 ${变量名} 引用:

pipeline:
  variables:
    input_path: "data/roads.shp"
    buffer_dist: 500
    output_crs: "EPSG:4326"

  steps:
    - id: load
      use: io.read_vector
      params:
        path: "${input_path}"          # → "data/roads.shp"

    - id: buffer
      use: vector.buffer
      params:
        input: "$load"
        distance: "${buffer_dist}"     # → 500 (数值类型)

    - id: reproject
      use: vector.reproject
      params:
        input: "$buffer"
        target_crs: "${output_crs}"    # → "EPSG:4326"

5.1.2 类型保留

当整个参数值是单一 ${var} 引用时,变量的原始 Python 类型被保留:

variables:
  buffer_dist: 500          # int
  tolerance: 0.001          # float
  auto_fix: true            # bool
  fields: ["name", "type"]  # list

# 传入步骤时类型不变
params:
  distance: "${buffer_dist}"    # → 500 (int)
  tolerance: "${tolerance}"     # → 0.001 (float)
  auto_fix: "${auto_fix}"       # → True (bool)
  required_fields: "${fields}"  # → ["name", "type"] (list)

5.1.3 字符串插值

${var} 嵌入字符串中时,变量被转为字符串拼接:

variables:
  project: "highway-2024"
  suffix: ".geojson"

params:
  path: "output/${project}${suffix}"   # → "output/highway-2024.geojson"
  layer: "buffer_${project}"           # → "buffer_highway-2024"

5.1.4 通过 --var 在命令行覆盖

--var 参数以 key=value 格式传入,值始终是字符串。框架会尝试进行类型转换(字符串→数字/布尔):

# 整数
geopipe-agent run pipeline.yaml --var buffer_dist=1000

# 浮点数
geopipe-agent run pipeline.yaml --var tolerance=0.005

# 布尔值("true"/"1"/"yes" 转为 True)
geopipe-agent run pipeline.yaml --var auto_fix=true

# 字符串路径
geopipe-agent run pipeline.yaml --var input_path=data/new_roads.shp

# 多个变量
geopipe-agent run pipeline.yaml \
    --var input_path=data/new.shp \
    --var buffer_dist=200 \
    --var output_path=output/new_buffer.geojson

注意:通过 --var 传入的值优先于 YAML 文件中 variables 的定义,且会修改 PipelineDefinition.variables 字典,影响所有引用该变量的步骤。


5.2 步骤引用:$step-id$step-id.attr

5.2.1 步骤引用基础

步骤引用用于将一个步骤的输出传递给后续步骤的参数。格式:

语法 等价形式 含义
$step-id $step-id.output 步骤的主输出(StepResult.output
$step-id.output 显式写法 同上
$step-id.stats 步骤的 stats 字典整体
$step-id.feature_count stats 中的 feature_count
$step-id.issues_count stats 中的 issues_count 值(QC 步骤)
$step-id.issues_gdf QC 步骤的问题要素 GeoDataFrame
steps:
  - id: load-data
    use: io.read_vector
    params:
      path: "data/roads.shp"

  - id: buffer
    use: vector.buffer
    params:
      input: "$load-data"           # 等价于 $load-data.output
      distance: 500

  - id: save
    use: io.write_vector
    params:
      input: "$buffer"              # 等价于 $buffer.output
      path: "output/result.geojson"

5.2.2 访问统计属性

每个步骤执行后,StepResult.stats 字典的键可直接作为引用属性访问:

steps:
  - id: load-data
    use: io.read_vector
    params:
      path: "data/roads.shp"

  - id: check-geometry
    use: qc.geometry_validity
    params:
      input: "$load-data"
      auto_fix: false

  - id: auto-fix
    use: qc.geometry_validity
    params:
      input: "$load-data"
      auto_fix: true
    # 只有当几何问题数 > 0 时才执行
    when: "$check-geometry.issues_count > 0"

outputs:
  # 在 outputs 中引用统计数据
  total_features: "$load-data.feature_count"   # stats.feature_count
  bad_geometry:   "$check-geometry.issues_count"

5.2.3 StepResult 结构

理解步骤引用需要了解 StepResult 的数据结构:

@dataclass
class StepResult:
    output: Any = None          # 主输出(GeoDataFrame、文件路径等)
    stats: dict = {}            # 统计信息(feature_count、crs 等)
    metadata: dict = {}         # 元数据(driver、bands 等)
    issues: list[QcIssue] = []  # QC 问题列表(仅 QC 步骤填充)

属性访问规则

  • $step.outputresult.output
  • $step.statsresult.stats(返回整个 stats 字典)
  • $step.feature_countresult.stats["feature_count"](先查 stats)
  • $step.crsresult.stats["crs"](然后查 metadata)
  • 若以上都找不到,抛出 VariableResolutionError

5.2.4 各类步骤的输出结构

IO 步骤(io.read_vector)

StepResult(
    output = GeoDataFrame,      # 读取的地理数据
    stats = {
        "feature_count": int,
        "crs": str,
        "geometry_types": list,
        "columns": list,
    }
)

引用示例:

  • $load.output → GeoDataFrame(传给下一步的 input
  • $load.feature_count → 要素数量
  • $load.columns → 属性字段列表

矢量步骤(vector.buffer)

StepResult(
    output = GeoDataFrame,      # 缓冲区结果 GeoDataFrame
    stats = {
        "total_area": float,    # 缓冲区总面积
    }
)

IO 写入步骤(io.write_vector)

StepResult(
    output = str,               # 输出文件路径(字符串)
    stats = {
        "feature_count": int,
        "output_path": str,
        "format": str,
    }
)

QC 步骤(qc.geometry_validity)

StepResult(
    output = GeoDataFrame,      # 原始输入数据(透传)
    stats = {
        "issues_count": int,
        "valid_count": int,
        "issues_by_severity": dict,
    },
    issues = [QcIssue, ...]    # 问题列表
)

QC 步骤专有属性:

  • $check.issues_count → 问题数量
  • $check.issues_gdf → 包含问题要素的 GeoDataFrame
  • $check.issues → QcIssue 对象列表(不可直接序列化,一般不在 YAML 中引用)

5.3 引用解析顺序与规则

context.py 中的引用解析逻辑:

参数值处理顺序:
1. 非字符串值 → 直接返回(保持原类型)
2. 以 "$" 开头且非 "${" → 步骤引用
   2a. "$step-id"       → 查找 step-id 的 output
   2b. "$step-id.attr"  → 查找 step-id 的 stats/metadata 中的 attr
3. 包含 "${" → 变量替换
   3a. 整个值是 "${var}" → 返回原始类型
   3b. "${var}" 嵌入字符串 → 字符串插值
4. 其他 → 原样返回

注意事项

  • 步骤引用只能引用已执行步骤的输出,不能向前引用
  • 步骤 ID 中的连字符 - 在引用中需要保留($load-roads,不能写成 $load_roads
  • $step-id.attr. 只分割第一个,因此 $step-id.stats.feature_count 会尝试访问 stats 中的 stats.feature_count 键(通常不存在),正确写法是 $step-id.feature_count(直接访问 stats 键)

5.4 在 when 条件中使用引用

when 表达式也支持步骤引用和变量替换:

steps:
  - id: check-issues
    use: qc.geometry_validity
    params:
      input: "$load-data"
      auto_fix: false

  # 条件1:基于步骤 stats 属性
  - id: fix-issues
    use: qc.geometry_validity
    params:
      input: "$load-data"
      auto_fix: true
    when: "$check-issues.issues_count > 0"

  # 条件2:基于变量
  - id: simplify
    use: vector.simplify
    params:
      input: "$fix-issues"
      tolerance: 10
    when: "${enable_simplify} == true"

  # 条件3:复合条件
  - id: strict-fix
    use: qc.geometry_validity
    params:
      input: "$load-data"
      auto_fix: true
      severity: "error"
    when: "$check-issues.issues_count > 10 and ${strict_mode} == true"

5.5 在 outputs 中使用引用

outputs 声明支持完整的引用语法:

outputs:
  final_result: "$save-result"            # 输出文件路径
  feature_count: "$buffer.feature_count"  # 缓冲区要素数
  total_area: "$buffer.total_area"        # 缓冲区总面积
  crs: "$reproject.crs"                   # 最终坐标系
  qc_issues: "$check-geo.issues_count"    # 质检问题数量

这些值出现在 JSON 报告的 outputs 节中,可供 AI 或下游系统提取。


5.6 嵌套参数中的引用

对于接受字典或列表类型参数的步骤,引用也可以出现在嵌套结构中:

# 假设某步骤接受字典参数
params:
  config:
    input: "$load-data"          # 嵌套字典中的引用
    crs: "${output_crs}"
  field_list:
    - "$load-data.columns"       # 列表中的引用(按实际步骤支持情况)

5.7 常见引用错误

错误 1:引用未执行的步骤

steps:
  - id: buffer
    use: vector.buffer
    params:
      input: "$load-data"   # ❌ load-data 在 buffer 之后,尚未执行

  - id: load-data
    use: io.read_vector
    params: { path: "data.shp" }

错误 2:引用不存在的属性

- id: process
  use: vector.buffer
  params:
    distance: "$load-data.record_count"  # ❌ io.read_vector 没有 record_count 属性
                                          # ✅ 应改为 feature_count

错误 3:ID 拼写不一致

steps:
  - id: load-roads       # ID 使用连字符

  - id: buffer
    params:
      input: "$load_roads"  # ❌ 使用了下划线,与 ID 不符
      input: "$load-roads"  # ✅ 正确

5.8 本章小结

本章深入讲解了 GeoPipeAgent 的两种核心数据传递机制:

  1. 变量替换 ${var}:引用 variables 中的值,整个值时保留类型,嵌入字符串时做字符串插值
  2. 步骤引用 $step-id$step-id$step-id.output 的简写,.attr 可访问 stats/metadata 键
  3. 类型保留:单一 ${var} 引用保留原始 Python 类型,确保步骤参数类型正确
  4. when 条件:支持完整的引用语法,通过 AST 白名单安全求值
  5. StepResult 结构:理解 output/stats/metadata/issues 各字段是正确使用引用的基础

下一章将讲解条件执行、自动重试和错误处理策略的详细用法。


导航← 第四章:YAML 流水线格式第六章:条件执行与错误策略 →