
简介面向GIS与水文分析学习者的一份完整DEM河流提取实践包聚焦如何从数字高程模型中自动识别与提取河网。内含可直接运行的DEM_Water_Analysis.exe、配套的dem-data.txt测试数据以及用C#编写的核心算法源代码帮助用户从数据准备、流向计算到阈值筛选走通全流程。包内共81个文件、约3.12MB主要文件类型包括exe可执行程序、cs源码、jpg效果图、txt算法说明及dll依赖库结构清晰既适合课堂实验也方便二次开发。程序指南与算法说明文档详细讲解了D8流向法和Flow Accumulation累积量原理并给出参数设置建议结果截图对比了不同汇流阈值20至10000下的河流提取效果便于直观评估算法参数的影响。目前已有538人学习下载对于想掌握GIS水文分析、DEM地形分析或河网提取方法的用户是一份可直接上手、可验证的参考资料。1. 基于DEM数据的河流提取不只是填洼和流向那点事做GIS水文分析的人多半都有过这种经历拿到一份DEM想提取河网结果跑出来的河道要么断断续续要么在平原地带乱窜甚至把山脊线当成了河流。市面上讲水文分析的教程不少但真正能把手上的DEM数据变成可用河网、还能导出shp的完整流程很多资料都只讲了半截。这份基于DEM数据的河流提取资源解决的就是从原始DEM到矢量河网的全流程问题包含填洼处理、流向计算、累积流量阈值调整、河网分级以及矢量化导出适合正在做水文分析、洪水模拟或流域划分的GIS从业者也适合刚接触ArcGIS水文模块但不想在参数上调来调去的新手。它不玩虚的直接给出一条能跑通的路。2. 水文分析的核心逻辑为什么DEM不能直接用来提取河流2.1 原始DEM里的“坑”洼地、平地与伪河道很多初学者第一步就栽在“直接用DEM算流向”上。原始DEM中通常存在两类问题一是真实地形中的凹陷区域比如喀斯特漏斗或冰川侵蚀坑二是数据采集和插值过程中产生的伪洼地。这两类洼地都会让水流方向计算出现“死胡同”——水流到洼地里就停了没法继续向低处汇流提取出来的河网自然就是断的。常见做法是先做填洼Fill把凹陷区域抬升到与周围最低溢出口齐平。但填洼不是无脑点一下就行。填洼有个关键参数叫“Z值限制”默认情况下ArcGIS的Fill工具会填掉所有洼地包括那些可能是真实地形的凹陷。如果研究区是喀斯特地貌或者人工改造过的平坦区域填洼反而会让河道走向失真。我一般会先做一个试探性的流向分析看看原始DEM里的洼地分布规模再决定填洼阈值。填得太多河网密度会失真填得太少流向计算会断流。2.2 流向算法与累积流量D8、MFD与阈值的关系流向计算是河网提取的地基。ArcGIS默认用D8单流向算法每个像元只流向周围8个像元中坡度最陡的那一个。D8算法在陡峭山区表现不错但在缓坡或平原区域容易出现平行河道——因为坡度差异太小流向容易被噪声主导。另一种常用的是MFD多流向算法把水量按坡度比例分配给多个下游像元适合平坦区域但生成的河网边缘比较模糊矢量化后毛刺多。累积流量Flow Accumulation是把流向结果累加出每个像元的上游汇水面积。这里最难定的是阈值累积流量值大于某个像素数的像元才被认定为河道。阈值定得越小河网越密河道数量爆炸阈值越大河网越稀疏主干道明显但支流全没了。这个阈值没有固定标准跟DEM分辨率直接挂钩。比如30米分辨率DEM用500像素起步12.5米分辨率可能得用2000甚至更高。我一般会先用几个不同阈值试跑对比河网与真实水系在空间上的吻合度再定最终值。2.3 一份资源该包含什么从栅格到矢量的完整链条一份能落地的DEM河流提取资源至少得覆盖几步数据预处理填洼、消噪、流向计算、累积流量、栅格河网提取、河网分级Strahler或Shreve、栅格转矢量、以及矢量河网的平滑与简化。很多教程只讲前四步转矢量后就直接用结果生成的shp线要素上有一堆锯齿和伪分支。真正实操过的工程师都知道栅格河网转矢量之后还得做简化Simplify和消除短小伪河道Eliminate才能用于制图或进一步的水文模拟。这份资源的价值就在于它把上述链条完整串起来了并且给出了每个工具的参数参考值。它不是那种只丢一个脚本或者PPT的素材包而是把参数设置、处理顺序、常见报错和解决思路都整理好的实操笔记。拿到手后只要你的DEM数据格式没问题基本可以按图索骥少走好几天的弯路。3. 实操第一步填洼与流向计算的参数设置细节3.1 填洼工具的正确打开方式Z值限制与条件填洼打开ArcToolbox里的Spatial Analyst 工具→水文分析→填洼Fill界面很简单输入DEM、输出栅格还有一个可选参数“Z限制”。默认空着表示所有洼地都会被填平。如果研究区的地形相对完整只是数据有杂讯用默认值就好。但我更推荐一个习惯先用【焦点统计】或【栅格计算器】生成一份“疑似洼地”掩膜看看洼地的数量和空间分布再决定是不是全部填掉。案例某流域DEM有明显的大坝和水库区域直接填洼会把水库填成一个平面河流提取结果在水库下游断流或者错位。这种情况下不能全局填洼得先把水库区域掩膜出来填洼时用【提取分析】将水库区域排除再对剩余区域填洼。还有人用条件填洼即只填洼地深度小于某个值的凹陷比如小于10米填掉大于10米的保留。这个阈值没有定论跟DEM垂直精度和研究目的有关。做洪水淹没模拟的洼地通常保留做河网提取的填得干净些更好。填洼完成后别急着跑流向。我习惯先用栅格计算器做一个减法把填洼前后的DEM相减检查哪些地方被抬升了抬升幅度是否合理。如果某个区域抬升了50米以上那大概率有问题要么是真实地形要么是DEM有粗差。这一步虽然多花两分钟但能省掉后面流向计算的整段返工。3.2 流向计算D8算法与边缘处理流向工具位置Spatial Analyst 工具→水文分析→流向Flow Direction。输入填洼后的DEM输出流向栅格。这里有一个容易忽略的选项输出法线方向是可选的一般不勾选默认输出的是“流向编码”8个方向1,2,4,8,16,32,64,128这不是普通的方向角数值而是在学习栅格计算时容易搞混的点。后面做条件判断时比如要筛选东向流向不是找流向值等于90而是找等于64的像元。D8算法本身很简单计算中心像元与周围8个像元的高程差除以水平或对角线距离取最大坡降方向。但DEM边缘像元周围没有8个邻居ArcGIS会自动把它标记为无数据。如果你发现流向栅格边缘有一圈奇怪的值多半不是算法问题而是原始DEM的无效值没处理好。处理办法是在填洼前先用【按掩膜提取】把DEM范围严格限制在流域边界内或者用【栅格计算器】把无效值统一赋为NoData。边缘河网提取错误往往就是这一步埋下的雷。3.3 实测参数参考30米与12.5米DEM的设置差异不同分辨率的DEM推荐参数差异很大。以我处理过的几个项目为例DEM类型分辨率填洼Z限制流向算法累积流量阈值像素SRTM30米默认全部填平D8500~1000ALOS12.5米默认全部填平D82000~4000无人机雷达2米按需填洼保留真实坑塘MFD5000以上注意这个表格是参考值不是死规定。累积流量阈值跟DEM分辨率、地形坡度、降雨特征都有关系。坡度陡的山区汇水面积小也能形成河道阈值可以调小一点平原地区河道形成的汇水条件更严格阈值要调大。我见过有人拿着一份5米DEM阈值设500结果提取出来的“河网”把农田排水沟全画进去整个流域密密麻麻像毛细血管根本不能用。调阈值没有捷径只能多次尝试。一个技巧是先用一个较大的初值比如像素数的5%跑出主干河流再逐步降低阈值观察支流出现的趋势和断裂点。当阈值降到某个值之后河网长度会突然增多那往往就是伪河道的开始退回到上一个阈值即可。4. 河网提取与分级把栅格水线变成有层次的河道4.1 累积流量栅格的应用与阈值选取技巧得到流向栅格后用【水文分析】→【流量】工具计算累积流量。输入是流向栅格输出累积流量栅格单位为像元个数代表每个像元接收的上游汇水像元数。这个数值大小与汇水面积成正比换算成面积就是像元个数乘以单个像元面积。比如30米分辨率的DEM一个像元面积是900平方米累积流量1000就代表汇水面积0.9平方公里。阈值选取我一直用两步法第一步用【符号系统】把累积流量栅格按分位数分类显示比如分成5类看看累积流量在哪个区间突然稀疏选择这个断点附近作为初值第二步生成河网后与高分辨率影像叠加目视检查重点看河网是否穿过了山脊线、是否在平原区域消失。一个可复用的经验是阈值取DEM分辨率的10到20倍。例如30米DEM阈值取300到600像元12.5米DEM取1250到2500像元。如果河网断头太多降低阈值如果河道乱窜提高阈值。4.2 栅格河网转矢量之前必须做的清理操作用【栅格计算器】提取河网时常用公式是 SetNull(“FlowAcc” 阈值, “FlowAcc”)小于阈值的像元置空大于阈值的保留。这一步得到的栅格河网通常是8像素宽的粗线直接转矢量会得到面状要素或者带锯齿的线。正确的做法是先对河网栅格做【栅格河网矢量化】ArcGIS 10.8及以后版本有专门的“栅格河网矢量化”工具或者用旧版的“Stream to Feature”。无论哪个工具转换之前得先做两个操作一是【条件函数】将河网赋为常数1背景赋为NoData让河网形成连续区域二是用【区域连通性】检查河网是否有断裂弧段如果有需要调整阈值或者补做一次“填洼”。断裂的河网矢量化后会生成多条短线自动合并很困难只能人工在编辑里补线那事情就复杂了。4.3 河网分级方案选择Strahler与Shreve的区别河网分级有两个常见方案。Strahler分级规则没有支流的河段为1级两条同级河段汇合后级别加1不同级河段汇合后级别保持较大者。Shreve分级则简单粗暴所有源头河段为1级汇合后级别相加。Strahler适合反映河网的等级关系在地貌制图中常用Shreve更适合做水文模拟因为它能反映总汇水路径的贡献量。ArcGIS中对应工具是“河网分级”Stream Order输入河网栅格和流向栅格输出分级栅格。选哪种取决于你的用途。如果只是做流域边界或河道走向示意Strahler就行如果要做径流计算或洪峰分析Shreve会更合理。参数设置上还有一个容易被忽略的点输入河网栅格必须只包含河网像元通常值为1背景必须是NoData。很多人直接把累积流量栅格当作输入结果跑出来的分级结果全是背景值这是新手最容易翻车的地方。# 如果不想手动操作ArcGIS界面可以用arcpy脚本一键完成河网提取与分级 # 前提已安装ArcGIS Desktop或Pro并具备Spatial Analyst扩展许可 import arcpy from arcpy.sa import * # 设置工作环境 arcpy.env.workspace rD:/gis_data arcpy.env.overwriteOutput True # 输入文件——注意DEM必须是填洼后的 dem fill_dem.tif # 1. 流向计算 flow_dir FlowDirection(dem) flow_dir.save(flow_dir.tif) # 2. 累积流量计算 flow_acc FlowAccumulation(flow_dir) flow_acc.save(flow_acc.tif) # 3. 提取河网栅格阈值设为1000个像元约0.9平方公里 threshold 1000 river_rast SetNull(flow_acc threshold, 1) river_rast.save(river_rast.tif) # 4. 河网分级采用Strahler stream_order StreamOrder(river_rast, flow_dir, STRAHLER) stream_order.save(stream_order.tif) # 5. 栅格河网转矢量 stream_vec arcpy.sa.StreamToFeature(river_rast, flow_dir, stream_vec.shp)这段脚本的逻辑顺序很关键先算流向再算累积流量然后按阈值提取河网栅格之后做分级和矢量化。注意第5步StreamToFeature输出的是线要素类保存路径里不能用“stream_vec.shp”直接作为参数准确写法是先设定输出要素类变量再用arcpy.CopyFeatures复制。上面代码里的写法是为了清晰展示流程实际运行时建议把第三步阈值单独拉出来先用多个阈值试跑再落地脚本。另外阈值调整是这条链路里最影响结果的一环——阈值设小了河道太多设大了主干道断流。建议把阈值参数化循环跑几个值对比后再定。5. 避开河流提取过程中最常见的五个坑5.1 投影坐标系的错平面坐标与地理坐标混用导致面积失真现象用WGS1984地理坐标系直接跑水文分析提取出来的河网严重变形流向方向斜得离谱。原因流量累积和流向计算都是基于水平和垂直距离的比例关系地理坐标系的单位是度而高程单位是米两者直接计算坡降时水平距离和垂直距离不在同一量纲下算法认为几乎处处是陡坡。解决在做任何水文分析之前先将DEM投影到适合研究区的投影坐标系比如UTM或高斯克吕格。分辨率要留意一下投影后的像元尺寸会发生变化比如30米的SRTM投影到UTM北半球带像元尺寸可能变成29.7米累积流量的阈值也需要做相应微调。5.2 填洼过头把真实河槽填没了现象提取出来的河流路径平直完全不像自然弯曲的河道而且主流与明显河谷位置对不上。原因填洼工具的Z限制没设置把所有洼地都抬平了。自然河道本身就是一种“洼地”河槽的相对低洼被填掉后水流方向找不到原本的汇水通道只能沿着填平后的曲面乱走。解决改用带Z限制的条件填洼。在填洼工具中设置Z限制为比如10米根据DEM竖直精度只填埋深度小于10米的伪洼地保留深度大于10米的真实河谷然后重新计算流向。如果拿不准阈值可以做一组对比实验分别用0不填、5、10、20米观察提取结果与影像中河道的匹配度。5.3 累积流量阈值拍脑袋河网密度失控现象同一份DEM用1000阈值提取的河网有上百条支流用5000阈值却只剩下几条主干道两个结果都看起来不合理。原因阈值和DEM分辨率、地形起伏、研究区面积都有关系。有人用3000跑的太行山区很顺但同样的值拿到平原水稻田区域提取出来的全是沟渠网。解决不要直接拍一个阈值先画一条“累积流量-河网长度”的关系曲线。方法是在【符号系统】中用分位数分级显示累积流量栅格不断尝试阈值每次记录河网栅格中的像元数当阈值变化导致像元数跳变时那个点就是地形上的“河道源头”与“坡面径流”的分界。研究区是山区的这个跳变往往出现在累积流量500~2000之间平原区域会明显靠后可能到5000以上。实际项目中我会按这个跳变点再乘0.5~2作为试验区间对比影像决定。5.4 矢量化后短线纵横交错栅格河网没有先清理现象Stream to Feature生成的结果里有大量细碎的短线相互交叉有的地方看起来像蜘蛛网根本不是连续河网。原因栅格河网提取时河道不是单像素宽的而是多像素宽的条带。条带内部像元之间有多条可能的连通路径转矢量时会产生多条平行短线。同时水流在分叉处会生成多路径分支如果没有做“缩窄”处理短线自然满天飞。解决转矢量前先对河网栅格做一次“细化”Thin或者使用“河流链接”Stream Link工具先给每条河段一个唯一编号然后转矢量。另一种更稳妥的方法是直接把河网栅格转为“矢量化河网”工具ArcGIS Pro 2.7才有它内部已经做了中心线提取输出的线要素基本平滑。如果是老版本ArcGIS就用“Stream Order”先合并河段再转矢量能减少相当一部分交错短线。5.5 河网断头平原区提取结果频繁中断现象河道在平原区域走了一段就消失再往前一段又出现没法形成一个连续的河网。原因平原地区地形平坦流向算法的D8只能在8个方向上选一个可地形实际坡降方向往往介于两方向之间导致流向不稳定同一河段在不同像元上选出完全相反的方向。另外平原地区DEM的高程精度往往不能满足水文分析要求10米的误差就足以覆盖实际坡度。解决在填洼前先对DEM做平滑处理比如【焦点统计】3x3窗口取均值能抑制一些噪声。如果研究区确实地势平坦可以考虑用多流向算法MFD替代D8或者在流向计算前先对DEM进行“1米等高线平滑”之类的预处理。再一个土办法是把累积流量阈值降低一档让河道源头下探到低洼沟壑区域往往能接上断头但代价是支流增多后期需要手工删除一部分。6. 河网结果检验与实用技巧让提取结果更贴近真实水系6.1 与影像及现有水系数据叠加验证河网提取完成后第一件事不是急着出图而是拿结果叠加到Google Earth或高分影像上逐段对比。重点看几个地方主流是否贴合河谷底部支流源头是否出现在地形上的沟头位置平原区域的曲线是否与旧河道吻合。如果主流偏离了河谷大概率是流向计算阶段出了问题回头检查DEM预处理。如果只有支流局部偏通常是累积流量阈值偏低导致沟壑误判。有一个我常用的检验方法把提取河网和高精度遥感影像中的河流中心线做缓冲区对比计算两者重合率。重合率在70%以上算基本合格90%以上说明流程和参数设置很理想。重合率低于50%时不要动阈值了回归源头检查投影和填洼。6.2 用坡度与汇水面积关系过滤伪河道有些伪河道确实难以通过目视发现尤其是在山地阴影区。一个更客观的过滤方法是使用“坡度-面积”阈值判断。原理是真实河道的源头通常满足坡度与汇水面积的幂律关系即坡度较大的区域需要较小汇水面积就能形成河道而平坦区域需要很大汇水面积。如果你的河网里出现了一些既陡峭又汇水面积很小的短河道那多半是算法噪声。操作上可以先从累积流量栅格中提取每个河网像元的汇水面积再用【坡度】工具计算坡度然后做散点图。如果散点图上有大量短河道落在“高坡度低面积”区域把它们单独提取出来分析确认是噪声后用【按属性选择】从矢量河网中删除。6.3 批量提取多个小流域河网的自动化思路当研究区覆盖范围很大需要分多个子流域分别提取河网时手工操作会累死人。建议把前面的arcpy脚本封装成一个函数接收DEM路径、阈值、输出路径三个参数然后在循环里遍历多个裁剪好的DEM子集。# 封装一个单流域河网提取函数用于批量处理 def extract_river(dem_path, threshold, output_dir): 简单封装填洼-流向-累积流量-河网栅格-矢量化 dem_path: 单个子流域的DEM路径已裁剪 threshold: 累积流量阈值 output_dir: 输出文件夹 import arcpy from arcpy.sa import * arcpy.env.workspace output_dir arcpy.env.overwriteOutput True # 填洼默认填平全部洼地 dem_fill Fill(dem_path) dem_fill.save(dem_fill.tif) # 流向 flow_dir FlowDirection(dem_fill) flow_dir.save(flow_dir.tif) # 累积流量 flow_acc FlowAccumulation(flow_dir) flow_acc.save(flow_acc.tif) # 河网栅格 river_rast SetNull(flow_acc threshold, 1) river_rast.save(river_rast.tif) # 转矢量 river_shp r%s/river_%s.shp % (output_dir, str(threshold)) arcpy.sa.StreamToFeature(river_rast, flow_dir, river_shp) print(完成:, dem_path, 阈值:, threshold, -, river_shp) # 批量调用注意DEM子集要确保已在统一投影坐标系下 if __name__ __main__: dems [ D:/watersheds/sub1.tif, D:/watersheds/sub2.tif, D:/watersheds/sub3.tif ] for dem in dems: extract_river(dem, threshold1000, output_dirD:/watersheds/out)这个脚本在实测中有一个值得注意的参数Fill工具的默认行为会无条件填充所有洼地在多个子流域同时提取时某些子流域里可能存在大坝或水库会导致提取结果异常。稳妥的做法是在循环外面先对所有子流域做统一的掩膜将水体区域抠掉再交给批量脚本。另外阈值如果需要对不同子流域设置不同值可以把循环里的threshold改成读取一个配置表按DEM名称索引对应阈值。批量处理结束之后河网矢量文件会很多建议统一使用GDB地理数据库存放不要用散落的一个个shapefile。shapefile在编辑拓扑关系时容易出问题线要素属性表里的字段长度也有限制。GDB能更好地保持线区间连续性也方便后续做河网汇流累积等操作。从第一次把DEM填洼到最终得到像样的河网中间踩过的坑远不止上面列的那些。投影、填洼、阈值、矢量化、批量化每个环节都有它的脾气。尤其是阈值这个问题没有拿来即用的万能答案。从那以后我每次拿到一份新DEM都会强制自己先做一个快速试跑投影检查、填洼对比、坡度-面积分析、不同阈值出图比对整套流程走下来不超过半小时但能确保后面正式处理时不会在河网形态上返工。希望这份U盘里的资源和这篇实操笔记能帮你把DEM变成真正能出图、能建模的河网数据也希望你在跑通一次完整流程后能摸索出属于自己研究区的那套参数组合。希望帮到你。本文还有配套的精品资源点击获取