
说起来有点丢人我入行第一年接到一个流域地形分析任务对方扔给我一张纸质地形图让我把山脊线和山谷线“描”出来。当时我拿着铅笔在图纸上画了整整一个下午画到颈椎疼交上去还被复核打回来——因为我在两条山谷线之间漏了一条冲沟。后来我才彻底想明白ArcGIS里提取山脊线、山谷线从来就不是“画”出来的而是靠DEM配合水文分析和表面分析“算”出来的。而且这套计算流程一旦跑通用图解建模工具ModelBuilder封装成一个双击就能跑的工具再复杂的流域也能在几分钟内出全套结果。这篇文章我不会只给你罗列工具名称而是把我实际跑过的两条完整技术路线、每一步的参数选择逻辑、以及用ModelBuilder把它们打包成可复用工具的封装方式全部摊开讲一遍。适合刚接触ArcGIS水文分析的人照着做也适合已经会点基础操作、但想搞清楚“为什么阈值这么设”“为什么山脊线要用反地形”的进阶用户。1. 为什么山脊线、山谷线是“算”出来的两条路线的底层逻辑1.1 山脊线、山谷线的定义与实战价值先统一一下概念。山脊线在地貌学上叫分水线通俗讲就是地形上那道“屋脊”雨水落到山脊线两侧会流向完全不同的方向。山谷线则是汇水线是水流在谷底聚集、向下游流动的轨迹线。这两个要素在很多领域的价值都不用多说做流域划分时山脊线就是流域边界的骨架做地质灾害评估时山谷线是潜在泥石流和洪水通道就连搞电力选线、野外路线设计的人也要尽量避开水流汇聚的谷底沿山脊线布设才能减少填挖方。在ArcGIS里手动描线不是不行但一套流程跑下来你会发现手画线既不能复现也没法量化回答“为什么这条线画在这个位置”这样的质疑。尤其当项目涉及多个子流域、多期DEM对比时靠手画的一致性完全没法保证。所以业界普遍的选择就是用DEM自动计算山脊线和山谷线把主观判断变成客观算法。1.2 水文分析路线与表面分析路线的差异自动提取有两条主流路线核心逻辑完全不同。水文分析路线的基本假设是把雨水均匀地洒在地表上水会沿着坡面流动最终在低洼的谷底汇聚成河。所以ArcGIS先用流向算法计算每个像元的水往哪个方向流再根据流向计算汇流累积量——也就是每个像元“收集”了多少上游像元的水。累积量大的像元连线天然就是山谷线。想提取山脊线也有个取巧的办法把DEM整体乘一个-1反转过来原来的山脊就变成了“谷底”再走一遍完全相同的水文流程累积量大的位置就对应山脊线。表面分析路线不模拟水流它直接从DEM的局部几何形态入手计算每个像元处的曲率、坡向变化。地形在某个方向上弯曲的程度被量化成正值和负值正处于山脊、负处是山谷。这条路线的优势在于不需要水流连续性的假设对局部微地貌的反应更灵敏比如滑坡陡坎、人工边坡这样的细碎地形水文分析往往抓不到而曲率变化能明显看出来。1.3 图解建模工具在整条流程中的位置初次上手的人容易把注意力全放在单个工具上忽略了一个现实不管是哪条路线提取一整套山脊线和山谷线至少需要串联五六个工具。水文分析要Fill、Flow Direction、Flow Accumulation、Con、Stream to Feature表面分析要Curvature、栅格计算器、重分类、矢量化。每个工具都要重复设置输入输出和参数一旦要跑多个子流域、多期DEM手工点鼠标很快就会出乱子。图解建模工具ModelBuilder正是为这种多步骤流程准备的。它把工具链图形化数据和工具之间用箭头连接流程逻辑一眼就能看懂。更关键的是模型可以把关键输入暴露成参数交给别人用时对方只需要指定自己的DEM和阈值就能跑完全不用关心背后到底经过了多少个工具。这也是我坚持把提取流程封装成模型的原因——它不只是在提高效率更是在把个人经验沉淀成可复用的团队资产。2. 开工前的DEM准备数据质量决定结果上限2.1 数据源与分辨率选择提取山脊线和山谷线是个对数据较真的活儿。DEM来源五花八门ALOS 12.5米、SRTM 30米、ASTER GDEM 30米还有无人机航测出来的亚米级DEM选择标准主要看研究区尺度和你想要的地形细节。我的经验是大区域、地形起伏明显的山地用12.5米或30米的公开DEM就够了分辨率太高反而会引入大量树木、房屋造成的噪点提取出的线条碎得像蜘蛛网小流域、要做精细冲沟分析或灾后评估最好用无人机或激光雷达生成的亚米级、米级DEM。实测下来DEM的分辨率直接决定后续阈值的取值范围30米DEM里一个像元代表地面30米水流累积量阈值可能要上千而1米DEM里一个像元才代表1米阈值往往几百就够了。2.2 投影坐标系与填洼的边界情况还有一个容易翻车的细节做水文分析的DEM必须使用投影坐标系不能是WGS84那种以度为单位的地理坐标系。原因很简单流向算法要比较相邻像元之间的坡降如果X、Y单位是度而高程单位是米算出来的坡度完全没有物理意义。正规做法是先把DEM投影到适合本区域的投影坐标系比如国内常用Albers等积圆锥投影或者UTM分带投影确保水平单位和高程单位一致。2.3 Fill填洼参数与NoData处理填洼Fill是水文分析的前置步骤作用是把DEM里的凹陷sink填平不然水流会困在洼地里流不出去流向和累积量都会失真。Fill工具的z_limit参数值得认真对待。默认情况下z_limit为空意味着ArcGIS会填掉所有洼地这在大部分地区没问题但如果你的研究区有真实存在的坑塘、天坑、采石场它们会被当作虚假洼地一并填掉导致地形失真。实操时我会按DEM分辨率和区域特点设置一个合理阈值。比如在山地流域z_limit可以设为10米左右只填掉小于10米深的伪造洼地保留真实的大型凹陷当然这个值没有标准答案建议多试几组用原始等高线对照检查。还要提醒一下NoDataDEM边缘如果有无值区水文分析会把NoData当边界处理但边缘像元的流向计算经常出错建议先用边界矢量裁剪DEM或者用Con函数把边缘NoData补齐再进入流程。3. 水文分析路线实操山谷线、山脊线一次跑通3.1 D8流向算法与汇流累积量机制ArcGIS的Flow Direction工具默认用D8单流向算法。它会扫描每个像元周围3×3的窗口计算中心像元到8个相邻像元之间的最大坡降方向并把方向编码成2的幂次方1、2、4、8、16、32、64、128方便后续用位运算判断水的流向。Flow Accumulation工具接着读取这个流向栅格计算每个像元“上游”到底有多少个像元的水汇集过来。在没有权重栅格的情况下累积量就是流入该像元的上游像元数量。你可以把累积量理解成降雨后的径流强度数值越大代表这个位置越是水流汇聚的通道。这样累积量栅格里数值高的区域本质上就是水系骨架也就是山谷线的雏形。3.2 汇流累积量阈值怎么定累积量栅格只是数值连续的一张栅格图真正变成山谷线需要给它一个截断阈值。阈值做的事很简单累积量大于等于阈值的像元判定为河网小于阈值的舍弃。阈值越低提取出的山谷线越密集阈值越高剩下的越是主干线。这个阈值没有万能公式但有一个可复用的实战经验用“多级试算法”。先填一个看起来合理的初始值比如DEM像元分辨率30米时从1000起步10米分辨率时从500起步。跑完之后把结果叠加到山体阴影图上观察线条是否贴合真实沟谷。如果主谷对得上、但支沟全是断断续续的碎线说明阈值偏低如果主沟都被截断则阈值偏高。我通常会在500、1000、2000、5000各跑一遍肉眼选择最贴近目标水系密度的那个值。3.3 山谷线提取步骤山谷线的完整操作链路是Fill → Flow Direction → Flow Accumulation → Con或SetNull → Stream to Feature。具体来说第一步填洼。用Spatial Analyst工具箱里的Fill工具输入原始DEMz_limit按前面讨论的方式设置。第二步计算流向。Flow Direction工具输入填洼后的DEM输出流向栅格。注意这里的输出是整数栅格值域为1到128的8个方向值。第三步计算汇流累积量。Flow Accumulation工具输入流向栅格输出累积量栅格。第四步用栅格计算器生成河网二值栅格。表达式可以这样写Con(FlowAcc 1000, 1)这个表达式会把累积量大于等于1000的像元赋值为1其余像元变为NoData。如果用SetNull写法也一样SetNull(FlowAcc 1000, 1)第五步用Stream to Feature工具把河网栅格转成矢量线。工具的输入有两个河网栅格和流向栅格。流向栅格的作用是辅助判断哪些像元属于同一条线段从而把栅格连通域连成正确的矢量折线。输出要素类选一个shp或地理数据库要素即可。到这一步你拿到的山谷线是矢量线层基本可以直接用于制图和分析。3.4 山脊线提取反转DEM再走一遍山脊线的水文分析思路非常巧妙。先打开栅格计算器生成一个反地形DEM(DEM * -1)这个反转后的DEM把原来的山谷变成了高地把原来的山脊变成了“谷底”。对这个反转DEM再做一遍填洼、流向、累积量的完整流程此时累积量大的区域就是原来地形的山脊线。操作上和3.3完全一致只是第一步的输入DEM换成反转后的产物最后Stream to Feature输出的就是山脊线。这个方法之所以成立核心在于水文分析的对称性水往低处流的规律不会因为地形高低互换而改变。只要DEM精度够反转方法提取出的山脊线通常能比较准确地贴合真实分水岭。3.5 反转DEM方法并不是万能的必须坦白说反转DEM法有它天生的盲区。第一遇到大面积平坦地区时反转后会产生大量虚假的平坦洼地流向算法在这些区域会随机分配流向导致提取出来的“山脊线”是一堆直线段或平行线完全不贴合地形。第二如果原始DEM里有真实凹坑比如火山口、采石坑反转后就变成凸起可能会被误判成山脊。第三反转后的Fill过程会再次填平所有洼地而这个“洼地”恰恰对应原始DEM里的高峰和山脊顶点填得越狠山脊线顶点位置偏移越明显。所以我在实际项目中一般不会单独用反转DEM一个方法交差。最稳妥的做法是结合表面分析的结果做交叉验证后面第4章讲的方法正好补上这个短板。4. 表面分析路线实操用曲率把山脊山谷“切”出来4.1 Curvature工具输出的三个栅格怎么看表面分析路线的核心工具是Curvature曲率位于Spatial Analyst工具箱的Surface工具集下。它在一个3×3的局部窗口内拟合表面函数并计算二阶导数最终输出三个栅格整体曲率Curvature、剖面曲率Profile curvature和平面曲率Plan curvature。三个栅格的意义要区分清楚。剖面曲率是沿着最大坡度方向的曲率正值表示坡面沿水流方向凸起凸坡负值表示凹陷凹坡。平面曲率是垂直于最大坡度方向的曲率按ArcGIS帮助文档的习惯正值表示该像元位置的地表形态在水流方向上呈发散状态对应山脊负值表示水流汇聚对应山谷。整体曲率则是两者综合正值代表局部凸起负值代表局部凹陷。曲率类型含义正值对应负值对应整体曲率局部表面综合弯曲程度凸起山脊倾向凹陷山谷倾向剖面曲率沿最大坡度方向的弯曲凸坡凹坡平面曲率垂直于最大坡度方向的弯曲水流发散山脊水流汇聚山谷这里要特别提醒一点不同国内教材和网文对平面曲率正负号的说法并不完全一致有的写正值山脊有的写负值山脊。最稳妥的办法是提取结果后用识别工具在山脊和山谷的典型位置点几下确认你当前ArcGIS版本的符号约定再决定筛选条件不要拿网上的表达式无脑套。4.2 用栅格计算器生成山脊线和山谷线栅格确认符号约定之后用栅格计算器做筛选。以“平面曲率正值对应山脊”为例山脊线栅格可以这样提取Con(Plan 0, 1)山谷线栅格则用Con(Plan 0, 1)这一步直接把曲率栅格转成了二值栅格接下来可以再用Stream to Feature矢量化也可以先做栅格后处理。需要说明的是曲率栅格是浮点型数值波动非常大直接在0值附近截断出来的线条往往毛糙、碎片化。所以实操时我经常设置一个缓冲带比如山脊条件写成Con(Plan 0.001, 1)、Con(Plan -0.001, 1)宁可漏掉一部分极缓地形也要保证提取结果不是满屏噪点。4.3 用坡度、坡向做二次筛选减少伪线这是表面分析路线中最容易被忽略的一环。曲率栅格对任何微小地形起伏都敏感平地里的一个蚂蚁窝、一条旧田埂都会被算成“山脊”或“山谷”。为了剔除这些伪线我习惯在提取时叠加一个坡度掩膜。思路是这样先做坡度分析生成坡度栅格。然后用栅格计算器限定“只有坡度大于某个阈值的区域才参与山脊线/山谷线提取”比如Con((Plan 0.001) (Slope 5), 1)坡度阈值的取值要看地形复杂度平缓丘陵可以用低到2度到3度高山区域可以提高到10度以上。这个办法能有效滤掉平地上的噪声保留真正的地形折变线。同理坡向也可以用来辅助判断北半球的阴坡和阳坡植被差异大有时会造成DEM上出现伪山脊叠加坡向分布可以人工筛查这类系统性误差。4.4 曲率法的精度问题与适用场景曲率法提取的线条在局部尺度上和DEM细节贴合得很好但它缺少水文分析那种“从源头到下游”的整体连贯性所以提取结果经常出现碎线、断线很少有一条完整的谷线从源头通到沟口。这也是它不像水文分析那样广泛用于河网提取的原因。但是曲率法有个水文分析无法替代的优势它不依赖水流方向的传递对洼地、平坝、人工地貌都不敏感在填洼很难处理的喀斯特地区、平原微地貌区曲率法往往是唯一能给出有意义结果的办法。如果你处理的地形以沟壑密度大、沟谷短浅的黄土高原或丘陵区为主曲率法甚至能提取出水文分析根本看不到的浅沟和细沟体系。5. 用ModelBuilder把整套流程封装成可复用工具5.1 为什么要上ModelBuilder如果你只是临时分析一个流域手动跑流程完全没问题。但项目的真实情况往往是同一个工作流要套在五六个子流域DEM上跑或者下个月数据更新了要全部重算一遍。这时候手动点工具工作效率极低而且容易在重复操作中漏掉某一步。图解建模工具ModelBuilder就是为这种重复劳动准备的。它能把你做过的工具链保存成一个有向图工具之间通过数据变量连接。下次运行只需要换一个输入DEM点一下执行整条流水线自动跑完。更妙的是模型可以被其他同事直接使用——他们不需要理解Curvature和Flow Accumulation的差异只需要把DEM拖进去设置阈值等着拿结果。5.2 搭建模型的具体步骤以水文分析提取山谷线为例模型搭建过程可以拆成六步。第一步新建工具箱和模型。在Catalog窗口里右键你的工作目录选择新建工具箱然后在工具箱上右键新建模型进入ModelBuilder编辑界面。第二步往画布里拖入工具。在右侧搜索窗口搜索Fill把它拖进画布接着依次搜索并拖入Flow Direction、Flow Accumulation、Con、Stream to Feature。第三步连接数据。从ArcCatalog里把准备好的DEM拖入画布用连接箭头连到Fill工具Fill输出的绿色椭圆是数据变量再把它连到Flow Direction工具以此类推把整个链路串起来。注意Con工具需要写表达式可以直接右键Con工具打开参数面板在条件表达式里填写FlowAcc 阈值并把输入栅格设为累积量栅格。第四步处理中间数据。默认情况下每个工具的输出都是临时文件关闭模型后会被清理。想保留中间栅格右键对应的绿色输出变量取消勾选“中间数据”并给它指定一个保存路径。我个人的习惯是把防洪DEM、流向栅格和累积量栅格都保留下来方便后续排查问题。第五步暴露模型参数。想让模型变成像普通工具一样带参数对话框可以右键DEM输入变量勾选“模型参数”Flow Accumulation后面的阈值在Con表达式里不太好参数化建议直接把累积量栅格变量右键设为模型参数这样每次运行前可以自由选择不同的累积量结果。运行模型时ArcGIS会自动弹出参数窗口使用者只需指定DEM和阈值相关输入。第六步验证并运行。在ModelBuilder菜单栏点验证Validate工具元素上出现红色感叹号就说明有参数没设置完整验证通过后点运行整条链路的执行进度会实时显示。5.3 参数化与迭代器批量处理模型搭好之后如果一次要处理几十个分幅DEM还可以用迭代器实现批量处理。右键模型画布空白处选择插入→迭代器→迭代栅格然后设置包含所有DEM的文件夹路径和通配符比如*.tif。这样模型每次循环会依次读取一个栅格作为输入整个流程自动跑一遍。迭代结果还可以用行内变量替换的方式自动命名输出比如在原DEM文件名后加“_valley.shp”避免文件互相覆盖。这个功能一开始会觉得有点绕但跑通一次后你会非常依赖它。我去年做某区域200多个1:1万图幅的山谷线提取就是用迭代器把整个流程自动跑完的大概一个多小时全部出完中间还能腾出手干别的活。5.4 从ModelBuilder到Python脚本ModelBuilder的本质是把可视化的操作翻译成geoprocessing调用所以它和arcpy Python脚本是互通的。在ArcGIS Pro里你可以直接把模型导出为Python脚本然后再修改扩展变成更灵活的批处理脚本。导出的脚本结构大概是这样的import arcpy from arcpy.sa import * arcpy.env.workspace rD:\DEM arcpy.env.overwriteOutput True dem dem_clip.tif dem_fill Fill(dem) flow_dir FlowDirection(dem_fill) flow_acc FlowAccumulation(flow_dir) river_grid Con(flow_acc 1000, 1) valley_lines StreamToFeature(river_grid, flow_dir, valley.shp)这个脚本其实比模型更简洁而且可以用for循环批量处理几十个文件。我的建议是先从ModelBuilder学起跑通逻辑之后再看导出的arcpy代码你会发现自己对ArcGIS地理处理的理解会往上跳一个台阶。6. 两条路线的实测对比与结果后处理6.1 同一块DEM两种方法跑出来差多少我把同一块典型山地DEM分别用两条路线提取山谷线放在同一张图里对比差异非常明显。水文分析提取的谷线连续性极强主沟能从上到下贯通支沟和主沟的汇流关系也基本合理曲率法提取的谷线局部贴合地形细节更精细但整体连贯性差经常在途中断掉需要大量人工拼接。山脊线的对比则反过来了。水文分析用反转DEM提取的山脊线往往过于平滑把小山脊、次级分水岭全部吞并曲率法提取的山脊线保留了更多分叉细节尤其在地形破碎区域效果突出但碎线也更多。对比维度水文分析路线表面分析路线山谷线整体连续性很好一般碎线多山脊线细节保留一般较好平坦地区表现差流向不可靠相对稳定喀斯特/人工地貌需要大幅填洼修正可直接处理对DEM分辨率敏感度高中高结果后处理工作量少多6.2 按地形和用途选路的决策表实际操作中怎么选我的经验是三类情况大流域河网提取、做水文模型汇流参数时无脑选水文分析。因为这类场景需要的是从源头到河口的连续线并且河网要能和实测水系对应上。山地小流域的微地貌研究比如滑坡沟道识别、冲沟发育分析优先用表面分析。因为它能保留局部折变线刚好对应小冲沟和陡坎。最理想的办法是两条路线都跑取长补短。在山谷线提取中水文分析的谷线质量高可以在曲率法结果上叠加水文分析谷线用后者修复前者的断线在山脊线提取中用水文分析得到的主分水岭做骨架再人工参考曲率法细节补充次级分水岭。6.3 结果后处理必做的几道工序不管是哪条路线矢量化之后通常都不会给你一条干净利落的线。最后必须做的后处理工序有三个。第一去掉异常短线和碎片。用Select By Attributes或按长度字段筛选删除明显不成比例的短线段。很多碎片是DEM噪点造成的删掉就好了。第二修复线之间的拓扑错误。相邻分带或分幅DEM提取的线接边处经常出现差一点没接上的情况。可以在ArcGIS里用自动捕捉或手工编辑把断点接上也可以转成geodatabase要素类后用拓扑修复工具处理。第三制图符号化。山脊线和山谷线是地貌制图的常客建议山谷线用蓝色实线、山脊线用棕色虚线并叠加山体阴影做底图出图效果会很专业。符号化之后把线层叠加到等高线上检查一遍看看是否有明显跨界或穿谷的现象。7. 踩坑记录与最终建议7.1 最容易被忽视的三个坑我在这套流程上踩过的坑不少挑三个最典型的说一下。第一个坑是单位不一致。有一个项目我导入了一份以度为单位的DEM填洼、流向都跑了结果累积量栅格出现大面积异常排查了一个多小时才发现是坐标系没投影。水文分析前检查投影坐标系应该成为肌肉记忆。第二个坑是填洼的z_limit设得太大。默认填掉所有洼地听着省事但在喀斯特区域很多真实的漏斗状洼地被填平后提取出的山谷线明显偏离真实冲沟位置。现在我做喀斯特区项目会专门用z_limit5到10米来限制填洼深度。第三个坑是ModelBuilder里忽视中间数据。第一次建模型时我没把累积量栅格设为中间数据关掉模型后想复查阈值发现中间结果全没了只能重跑整个流程。现在凡是可能复用的中间结果我都直接在模型里设置固定保存路径。7.2 我的最终建议如果你只记一条那我建议是别把希望全押在单一方法上。水文分析适合整体骨架表面分析适合局部细节两者是互补关系而不是替代关系。跑完结果后一定要叠加到山体阴影或等高线上去目视检查不要让黑盒流程替你决定哪些线条是合理的。我个人现在的一套标准作业流程是这样先用ModelBuilder封装一套水文分析模型提取主谷线和主山脊线再用曲率法跑一版细节线两者叠加后人工处理冲突部位最后把成图交给制图组出图。整个过程从原来的一天变成两小时而且换一个流域、换一期DEM都能复用同一套模型。希望这篇文章能帮你少走我当年那些弯路把山脊线、山谷线提取这个看似复杂的问题真正变成一套顺手就能跑的生产工具。