基于FME的伪节点检查
基于FME的伪节点检查
伪节点(Pseudo-node)指的是图上一段连续的线被人为打断成两段——两条线端点坐标完全一致、属性相同,本应合成一条线,却在生产过程中被断开。FME 做的就是把这些伪节点通过端点叠置 + 属性配对自动查出来。
检查规则
伪节点的判断取决于线的类型:
- 普通线—— 两条线端点咬合 + 属性一致 → 伪节点
- 有向线(如单线河流等)—— 除端点咬合、属性一致外,还要求两条线的方向一致(一条的终点接另一条的起点)
FME 实现
本文FME实现的整体流程如图:
以下只讲核心逻辑部分(Reader → 线过滤 → 数据预处理 → 检测 → 输出),Reader/Writer 部分每个工作空间大同小异。
1. 线要素过滤
读入线图层后,先用 GeometryFilter 筛掉非线几何(点、面、注记等混入的),再用 ClosedCurveFilter 剔除闭合线(闭合线没有伪节点可以检查),只保留非闭合的折线进入后续流程。
2. 属性匹配参数
参与匹配的字段:如GB(国标分类码)、CLASID(要素分类编码)、NAME(名称)、ELEV(高程值)、fme_feature_type(源图层名)等,可根据实际情况选择。通过命令行参数$(line_attrs)传入工作空间。
注意这里的传递链路:命令行宏$(line_attrs)不能直接用在 Matcher 或 AttributeConcatenator 里(编译期展开后会被当成字面字符串处理),必须经 ParameterFetcher 拉入数据流,生成属性_attrs。AttributeConcatenator 的 ATTRS 参数填@Value(_attrs)才能正确读到字段名列表,逐个提取字段值后用逗号拼接成_attrvs,最终由 Matcher 匹配这个属性字符串。
参数传递链路: --line_attrs "GB CLASID NAME ELEV fme_feature_type" ↓ ParameterFetcher → 数据流属性 _attrs = "GB CLASID NAME ELEV fme_feature_type" ↓ AttributeConcatenator: ATTRS = @Value(_attrs) → 读取 5 个字段的值,逗号拼接 → 数据流属性 _attrvs = "gb值,clasid值,name值,高程值,图层名" ↓ Matcher: MATCHATTRS = _attrvs3. 起点终点坐标提取
利用CoordinateExtractor 提取线段起、终点坐标,输出两条属性(以2D举例):
_x1/_y1— 起点坐标_x2/_y2— 终点坐标
4. 生成端点要素
利用 VertexCreator + AttributeCreator 转换坐标属性为点,新建PointType属性作标识(StartPoint/EndPoint):
| 通道 | 读取坐标 | 创建点 | 标注 |
|---|---|---|---|
| 起点 | _x1/_y1 | VertexCreator | AttributeCreator →PointType = "StartPoint" |
| 终点 | _x2/_y2 | VertexCreator_2 | AttributeCreator_2 →PointType = "EndPoint" |
每个原始线要素被拆成 2 个点要素,各自携带参与匹配的字段和PointType(点类型)属性。
5. 点叠置分析
PointOnPointOverlayer 同时接收起点和终点两个端口,设置容差0.001(根据实际需求),输出_overlaps表示该位置上除自身外还有几个其他端点。
6. 重叠端点筛选 + 几何配对
FME 2020 的 PointOnPointOverlayer 中,_overlaps记录的是该位置除了自己之外还有几个端点。值为 1 时表示该位置共有 2 个端点,刚好构成伪节点的候选条件。值为 0 为悬挂点,值大于1表示多段线交汇,不在此次检查范围内。
这步筛选后的点进入 Matcher。Matcher 做 2D 几何匹配(MATCH_GEOMETRY 2D),容差同样0.001,属性匹配作用于 AttributeConcatenator 拼接后的_attrvs属性(包含 5 个字段的拼接值)。注意,此处需要新建列表P{},用于将配对的两条端点的 PointType 存入(列表名可自定义)。
7. 分路判断与标记
匹配成功的配对进入两层 Tester 分流:
Tester_2— 区分有向线和普通线。$(yxx_code)参数传入有向线的 CLASID 编码列表:
- PASSED(CLASID 在有向线范围内) → 进入 Tester_3 验证方向
- FAILED(非有向线) → 直接送去标记伪节点,不需要做方向验证
Tester_3— 有向线的方向验证。判断P{0}.PointType != P{1}.PointType:
- PASSED(配对的两个端点一个是起点、一个是终点)→ 两条线的方向一致 → 真伪节点,标记输出
- FAILED(配对的两个端点同向)→ 有向线在该点处方向矛盾(首对首或尾对尾),另一种错误,标记输出
此处注意区分:伪节点是线被打断但方向能衔接,方向矛盾是两条本应首尾相接的有向线出现了首首/尾尾相接,属于有向线特有的错误。
最后 AttributeCreator_3 写入检查结果属性,Writer 输出到指定 Geodatabase。
注意事项
- 容差匹配— PointOnPointOverlayer 和 Matcher 的容差保持一致(0.001),两者不一致会导致 geometry match 通过但 overlay 没识别,产生误报。
- 属性拼接链路— 命令行参数经 ParameterFetcher 拉入数据流后,AttributeConcatenator 用
@Value(_attrs)读取字段名列表,再拼接成_attrvs给 Matcher。不能用$(line_attrs)直接填进 AttributeConcatenator——宏展开后被当字符串处理,不会做字段拼接。 - 闭合线处理— 闭合线没有伪节点,提前用 ClosedCurveFilter 排除,以减少运算量。
yxx_code参数— 在此填写有向线的 CLASID 编码(多个用","分隔),Tester_2 用来区分有向线和普通线。有向线需额外验证方向一致性,非有向线直接判定。
FME 转换器参数速查表
| 步骤 | 转换器 | 关键参数 | 备注 |
|---|---|---|---|
| ①线过滤 | GeometryFilter | FROM GeometryFilter_LINE | 保留 fme_line 几何 |
| ②闭合线排除 | ClosedCurveFilter | CHECK_CLOSURE_2D | CLOSED 跳过,OPEN 进下一步 |
| ③属性拼接 | AttributeConcatenator | ATTRS=@Value(_attrs) →_attrvs | 5 字段拼接成属性标识串 |
| ④坐标提取 | CoordinateExtractor | Mode=Start and End Points | 输出_x1/_y1和_x2/_y2 |
| ⑤创建起点 | VertexCreator + AttributeCreator | _x1/_y1→ PointType=StartPoint | 带起点坐标的点要素 |
| ⑥创建终点 | VertexCreator_2 + AttributeCreator_2 | _x2/_y2→ PointType=EndPoint | 带终点坐标的点要素 |
| ⑦点叠置 | PointOnPointOverlayer | Tolerance=0.001, MERGE_ATTRS=NO | 检测端点重叠,输出_overlaps |
| ⑧端点筛选 | Tester | _overlaps = 1 | 保留刚好2个端点重叠的位置 |
| ⑨几何配对 | Matcher | MATCH_GEOMETRY 2D, P{}列表 | 几何+属性双重匹配,输出 P{} 列表 |
| ⑩线型分流 | Tester_2 | CLASID IN $(yxx_code) | 有向线→验方向,非有向线→直通标记 |
| ⑪方向验证 | Tester_3 | P{0}.PointType != P{1}.PointType | 仅对有向线,验证方向是否首尾衔接 |
| ⑫标记输出 | AttributeCreator_3 + 4 | 问题描述=“存在伪节点” / “有向线在此处方向矛盾” | 两种错误分别标注 |
伪节点的逻辑很直接——几何位置 + 多属性匹配 + 线型分流。非有向线接上就算,有向线还要验证方向是否一致。
