简介:这份中国三级流域数据集是面向GIS、水文与地球物理研究者的矢量地理数据包,它将全国水系按一、二、三级流域层级进行系统划分,并为每个流域赋予唯一编码,可用于水资源配置、洪水风险区划、生态保护区划定及地下水动态研究。数据包采用Shapefile标准格式,共8个文件、约4.09MB,其中.shp存储流域边界多边形,.dbf记录流域名称与编码等属性,.prj定义坐标系,.shx/.sbn/.sbx用于空间索引加速,.xml提供元数据,.jpg提供示意预览,构成一套可在ArcGIS、QGIS等平台直接加载的完整数据集。配合“重力与水文”方向,流域边界与重力场观测结合,可帮助研究者追踪水体汇流路径、估算地下水补给区与排泄区,为干旱区水资源可持续管理提供参考。目前已有4275人学习或下载,适合地理信息、水利工程及环境科学领域的科研人员与高校学生使用。
1. 中国三级流域:从一条河的分界,到一张行业通用的空间编码网
做水利、环保和国土空间规划的人,几乎都绕不开“中国三级流域”这个词。它看起来像一张水系图,实际却是一套把整个国土按汇水范围逐级拆分的空间编码体系:一级流域对应大江大河一级水系的控制范围,二级流域是主要支流的汇水区,三级流域再往下细化到中小河流和闭合单元。你拿到一块地、一个监测断面、一批排污口数据,第一步往往不是画河流,而是先问“这在哪个三级流域”。只有落进这个编码格子里,后续的水功能区划、生态流量核算、总量控制指标才能对上口径。这篇就讲清楚这套体系怎么用、数据怎么拿、分析和出图时有哪些躲不开的坑。
2. 三级流域划分的逻辑:从汇水原理到编码规则,工程人员实际上在操作什么
2.1 为什么不用水系图,反而用流域面——划分单元背后的汇水逻辑
水系图表达的是“水流经过哪里”,流域面表达的是“水从哪些地方流过来”。同样是长江中游的一段,河流图上是一条线,三级流域图上却是从分水岭向外推开的整片坡面。工程上做排污口影响分析、面源污染估算、雨洪管理调度,都需要知道某个点位的上游到底涵盖哪些区域。用流域面而非河流线,是因为水生态和水量管理的所有计算都以“汇水区域”为基本空间单元,而不是以河道中心线为单元。
我看到不少刚入行的朋友拿着河流干流图去裁剪行政区统计面积,最后发现两个数据在边界上对不齐,因为河流图是线状双线河,而流域边界是闭合曲面。三级流域划分的核心价值,就是把所有国土和无流区也纳入到某一个确定的编号下,让面积统计、编码匹配、上下游拓扑关系都有了可计算的载体。
2.2 三级结构到底分了几层:一级、二级、三级各代表什么尺度
整套体系自上而下分成三层。一级流域是宏观尺度的大江大河区,例如松花江、辽河、海河、黄河、淮河、长江、东南诸河、珠江、西南诸河、西北诸河这一级别,覆盖面积动辄几十万平方公里;二级流域是一级流域内的主要支流水系或独立入海的河流区域,比如黄河一级区内的渭河流域、伊洛河流域;三级流域则是二级流域内的更小汇水单元,通常以中小河流的集水区或者水文测站控制的区间划分,尺度从几百平方公里到几万平方公里不等。
实际使用中,三级流域是最常用的工作粒度。它比行政区稳定,比栅格汇水区可读,又比一级二级流域更贴合具体工程项目的空间范围。比如做县域水资源供需分析,一级流域太大,塞进一个县只覆盖局部,而三级流域往往能和乡镇边界形成很好的对照关系。
2.3 编码规则:一二级流域看前四位,三级流域是完整六位码
三级流域数据在属性表里一定有编码字段,常见形式是六位数字。前两位代表一级流域,中间两位代表二级流域,后两位代表三级流域。比如“010104”拆开看,前两位“01”是某个一级区,“0101”是它下面某个二级区,后两位“04”就是这个二级区里的三级区。这套编码与行政区划代码类似,但比行政代码稳定,因为流域边界不随行政区划调整而变。
编码不连续是常态,某个二级区只编了01、02、05,没有03、04,不代表缺失,而是当初划分时预留或跳过。做程序开发时不要假设后一位一定等于前一位加一,必须当作普通字符串处理。另外,很多公开数据的编码存在老版本与新版不一致,尤其是三级码段在局部调整过,使用时先确认数据版本,再做映射表,不要直接拿旧编码去新的统计数据里关联。
提示:拿到任何三级流域矢量文件,第一步先看属性表里有没有“代码”和“名称”两个字段。没有的话先补全字段映射,再谈空间操作。
3. 获取和准备三级流域矢量数据:公开数据源的筛选与边界清理
3.1 数据源选择与版本核对,别拿错成“流域分区”还是“河流水系”
公开渠道能拿到的流域边界数据有两类,一类是“水功能区划”相关的小流域边界,一类是“水资源分区”的流域边界。三级流域这个叫法通常对应后者。下载前先看元数据描述里的图层名称,例如包含“三级区”“流域编码”“HYBAS”这类字样的才是要的。如果下载回来打开全是细碎的河道线,说明拿成了水文数据,不是流域面。
拿到文件后先检查图层类型:必须是面状要素(Polygon),不能是线状。用QGIS打开,在图层属性里看要素几何类型。如果原始数据是CAD的闭合多段线,需要转成面;如果是栅格分类结果,要先栅格转矢量。工程做法是保留原始数据副本,另存一个专门用于分析的工作副本,避免多次编辑污染源数据。
3.2 用 GDAL/OGR 做投影统一和属性读取:三条命令解决80%的准备工作
不同来源的数据投影不统一,最常见的是CGCS2000经纬度、Krasovsky 1940、以及各地方坐标系。做面积计算前必须统一投影到适合本区域的投影或全国等积投影。我用GDAL处理时,习惯先读取源投影,再用ogr2ogr转换。
# 查看原始数据的投影信息 ogrinfo -so -al 三级流域_原始.shp | grep -A 1 "Geometry:" ogrinfo -so -al 三级流域_原始.shp | grep "ID\\|EPSG" # 转换到CGCS2000 兰伯特等积投影(适合全国面积统计) ogr2ogr -t_srs "EPSG:102025" 三级流域_EqualArea.shp 三级流域_原始.shp -overwrite # 查看属性字段的前几行,确认编码字段 ogrinfo -al -so 三级流域_EqualArea.shp | head -20第一条命令执行后,输出会显示几何类型是面还是线,以及当前坐标参考。第二条命令里的EPSG:102025是Albers等积投影,适合做全国尺度的面积统计,但如果你只做某个省,建议换成对应省份的CGCS2000 / 3-degree Gauss-Kruger分区投影,精度更高。第三条命令输出属性表结构,如果看不到编码字段,多半是数据被裁剪过,属性没带全。
很多网上教程直接跳过投影统一就做面积计算,结果用经纬度坐标算出的面积单位是平方度,数值再大都是错的。我的习惯是:任何涉及面积的统计,先检查投影是否为等积或本地投影,再动手。
3.3 裁剪与融合:把全国图裁成项目区,并处理切割碎片
实际项目拿到的往往是全国几百MB的面数据,做局部研究先裁剪到项目范围。常见做法是先用行政区边界做缓冲区,再裁剪流域,但这一步有个细节:流域边界不一定会完全落在行政区范围内,跨界的流域会被裁掉一大块,统计面积时就会偏小。正确做法是“先裁剪后融合”,把落在项目区内的流域多边形保留完整,而不按行政区边界硬切。
# 用项目范围裁剪,保留完整流域要素(使用 -spat 空间范围过滤) ogr2ogr -spat 116.0 38.0 120.0 42.0 -clipsrc clip_extent.shp \ 流域_裁剪.shp 三级流域_EqualArea.shp # 如果只需要某个特定编码的流域,用属性过滤 ogr2ogr -where "CODE3='010104'" 流域_单个.shp 三级流域_EqualArea.shp用-spat加边界坐标是最快的裁剪方式,但要注意这个范围是按原始坐标系来的,如果原始数据是经纬度,这里就填经纬度范围;如果已经投影,要填投影坐标范围。属性过滤适合明确知道目标流域编号的场景。裁剪完务必检查图层内要素数量,如果比预期少,很可能是因为边界穿越了目标流域,导致该流域要素没有被完整选中。
4. 在 Python 里把三级流域用起来:关联站点数据、清洗边界、算面积占比
4.1 用 GeoPandas 加载三级流域与监测点,做空间连接
日常处理我更习惯直接写 Python 脚本,因为涉及多个批次数据,用 QGIS 手动点反而容易错。GeoPandas 加载 shapefile 后,先用编码字段保证唯一性,再用 sjoin 把点坐标匹配到流域面上。
import geopandas as gpd # 加载三级流域面数据 basin = gpd.read_file("三级流域_EqualArea.shp") print(basin.columns) # 确认字段名,尤其是代码和面积字段 # 加载监测点坐标 points = gpd.read_file("断面监测点.shp") # 空间连接:每个点匹配一个三级流域 joined = gpd.sjoin(points, basin, how="left", predicate="within") # 检查没有匹配上的点,通常是点在面边界上或数据投影不一致 unmatched = joined[joined["CODE3"].isna()] print(f"未匹配点数量: {len(unmatched)}")sjoin 的 predicate 参数,within 要求点完全落在面内,对于正好压线的点会匹配失败;改成 intersects 能匹配边界上的点,但可能一对多,需要后续按面积或距离取最近值。实战里,监测断面通常位于河道中心线,而流域边界是分水岭,断面点理论上一定落在某个流域内。如果你的点大量落在流域外,先检查坐标系是否一致,再检查数据源里是否包含“无流区”。
4.2 计算每个三级流域的面积,并修正比例尺带来的误差
拿到匹配结果后,统计每个三级流域的要素数量或者面积。面积计算必须基于投影后的数据。如果原始数据是经纬度,GeoPandas 里先用 to_crs 转换,再计算面积。
# 如果数据是经纬度,先投影到等积投影 if basin.crs.is_geographic: basin_proj = basin.to_crs("EPSG:102025") else: basin_proj = basin # 计算每个三级流域的总面积(平方米) basin_proj["area_m2"] = basin_proj.geometry.area basin_proj["area_km2"] = basin_proj["area_m2"] / 1_000_000 # 按编码汇总 area_summary = ( basin_proj.groupby("CODE3")["area_km2"] .sum() .rename("area_km2") .reset_index() ) print(area_summary.head(10))注意,这里每计算一次面积前都要确认当前图层是否已经投影。很多人用同一个变量名反复覆盖,第一次投影成功,第二次又读入原始数据,导致面积结果突变。另一个常见问题是原始数据本身存在自相交的几何错误(invalid geometry),面积计算结果会是负数或异常大。处理办法是先用 buffer(0) 修复几何。
# 修复无效几何,避免面积计算出负数 basin_proj["geometry"] = basin_proj.buffer(0) area_summary = area_summary[area_summary["area_km2"] > 0]buffer(0) 是 GIS 里的经典玄学操作,它不会改变边界整体形态,但能清理几何拓扑错误。做完这步再看面积,异常值基本消失。
4.3 把离散点统计聚合到三级流域,生成一张可直接出图的表格
工程报告中常见的表格是“某三级流域内有多少个排污口、总处理能力是多少、对应哪个二级流域”。点匹配完成后,直接用 pandas 分组聚合就能得到。
import pandas as pd # 假设 joined 有 "CODE3", "NAME3", "capacity" 字段 result = ( joined.dropna(subset=["CODE3"]) .groupby(["CODE3", "NAME3"]) .agg( 点位数量=("name", "count"), 总处理能力=("capacity", "sum") ) .reset_index() ) # 输出成 CSV,供报告直接引用 result.to_csv("流域统计结果.csv", index=False, encoding="utf-8-sig")这段代码最后用 utf-8-sig 编码输出,是为了让 Excel 打开 CSV 不乱码。很多同行栽在编码问题上,明明 pandas 里看是中文,写出去就成大花字。输出路径不要带空格和中文目录,避免后期脚本调用时读不到文件。
5. 三级流域应用的避坑清单:数据、边界与统计口径里的 5 个常见翻车点
5.1 现象:面积总和与水利统计公报对不上,而且差得离谱
原因:使用的流域边界版本与国家统计口径不一致,或者把湖泊水面排除在流域外,或者投影没换。我遇到过用某开源流域数据算出来的某区面积比官方公报少 30%,就是因为开源数据把大型湖泊和省界外飞地都裁掉了。
解决:先看官方水资源公报里的“水资源分区面积”,再用官方发布的三级流域边界数据重新计算。如果拿不到发布版,就在自己的图层里把湖泊多边形重新并进去,并且用等积投影算面积。最稳妥的办法是用目视对比:把算出的面积最可疑的流域高亮,叠加遥感影像看边界是否包住了主要山脊线。
5.2 现象:同一个点匹配到两个三级流域,空间连接结果重复
原因:流域边界存在共享边或重叠面,尤其是当数据来自不同图幅拼接时,相邻流域的公共边没有完全重合,留出细长缝隙,或者互相压盖。
解决:检查重叠区域。GeoPandas 里可以用 overlaps 找出重叠的要素对,然后在 spatial join 时取最小面积的那一个作为唯一归属。实际业务不追求数学严谨,只需要一个确定归属的规则:优先按点所在位置最深处的流域匹配,重叠区域里按面积最大的流域归属。
# 找出重叠要素并处理 intersections = gpd.overlay(basin_proj, basin_proj, how="intersection") duplicates = intersections[intersections["CODE3_1"] != intersections["CODE3_2"]]一旦发现重叠,不要再继续用原始数据做统计,先修复边界。修复办法是用 dissolve 把内部共享边简化,或者干脆用官方的最新版本覆盖。
5.3 现象:属性表里的编码是 12 位数字,甚至带字母,不是六位
原因:拿到的不是“全国三级流域分区”,要么是更细的四级或五级流域,要么是某地方自己编码的子流域。地方编码经常带“J”“H”等水系字母,不能直接和一二级流域衔接。
解决:确认数据来源说明里的编码规则。如果是地方小流域,先建立地方编码与国家标准三级流域的对照表,再按国标聚合。编制对照表的方法:把地方流域的面与三级流域面做空间相交,按面积占比映射,占比超过 50% 的那个三级流域作为对应国标编码。
5.4 现象:小流域面积算出来是负数,或者为零
原因:几何环方向错误(outer ring 与 inner ring 方向颠倒)导致 polygons 面积计算为负,或者只有一个点的退化几何。
解决:清洗几何。用 buffer(0) 修复环方向,再过滤掉面积小于 1 平方公里的碎片。这些碎片大多是裁剪边界时产生的 sliver,不算真实流域。处理时不要直接把碎片的编码丢弃,因为可能影响总数,而是将它们合并到相邻最大面积的流域里,保持区域完整性。
5.5 现象:统计口径里“三级流域”与“县级行政区”之间存在一对多和多对一,图表怎么都对齐不了
原因:流域边界与行政边界不完全重合,一个县可能跨三个三级流域,一个三级流域也可能覆盖多个县。直接按流域拉行政区属性,必然产生重复计算。
解决:先明确统计主体。做水资源供需平衡时以流域为主体,行政区面积做“流域内的县”拆分处理;做发改口径的指标核算时以行政区为主体,流域属性做参考字段,不在总量上硬加。工程上常用“面积权重法”拆分:把县面积按三级流域边界切割,用每个切片的面积占比分摊指标。
6. 进阶玩法:把三级流域编码变成拓扑关系,用递归查询做上下游分析
6.1 建立从三级到二级再到一级的父子关系表
三级流域编码天然包含层级信息,只要把六位码拆成三截,就能构造出树结构。这是一个很实用的技巧,用来做上下游汇总、流域嵌套查询,比空间分析更快,尤其在数据量大时。
import pandas as pd area_summary = pd.read_csv("流域统计结果.csv", encoding="utf-8-sig") area_summary["CODE1"] = area_summary["CODE3"].str[:2] area_summary["CODE2"] = area_summary["CODE3"].str[:4] area_summary["CHILD"] = area_summary["CODE3"] # 生成父子表 parent_child = area_summary[["CODE1", "CODE2", "CHILD"]].drop_duplicates() parent_child.to_csv("流域层级关系.csv", index=False, encoding="utf-8-sig")这个表不是纯粹的空间关系,而是编码派生出的层次关系。对于一条河流来说,三级码的父子顺序并不代表上游到下游的方向,不能通过编码大小判断上下游,只能用来做包含关系汇总。上下游拓扑要用“出口断面连接”数据才能算。
6.2 用 SQL 递归 CTE 快速汇总所有上游三级流域的指标
如果你需要算某个三级流域出口的污染物通量,而这个值等于它自身以及所有上游三级流域的总和,SQL 递归查询天然适用。假设你有一张表basin_stats(code, value),再有一张upstream_downstream(from_code, to_code)描述直接上下游关系。
WITH RECURSIVE upstream_tree AS ( -- 初始:目标流域自己 SELECT code, value, code AS root_code FROM basin_stats WHERE code = '010104' UNION ALL -- 递归:找到所有流向上游的流域 SELECT u.code, u.value, t.root_code FROM basin_stats u JOIN upstream_downstream ud ON ud.from_code = u.code JOIN upstream_tree t ON ud.to_code = t.code ) SELECT root_code, SUM(value) AS total_upstream_value FROM upstream_tree GROUP BY root_code;这里的递归表upstream_tree的逻辑是:从一个出水口节点溯流而上,逐步加入它的直接上游,不断扩展直到没有新的上游。工程上真正需要的不是一段能跑的示例,而是理解两个前提:第一,你必须有一张可靠的“流域上下游关系表”,这张表通常从河网矢量数据构建,而不是从三级流域面自动生成;第二,递归深度受数据库限制,但三级流域层级最多也就几百个节点,一般不会触及递归上限。经过我多次验证,这个查询在十万行级数据上速度仍然很快,前提是从码、to码两列都建了索引。
6.3 一个收尾习惯:所有面积统计前,先画一张“面积与编码对应图”自查
我有个从小养成的习惯,每次拿到新版本流域数据,不做任何分析前,先导出全流域面积排序表,看一眼面积最大的几个三级流域是否落在水系发育的自然区域。如果排序结果出现异常,比如某个三级流域面积不到 1 平方公里却能排进前十,一定是数据有问题。这个习惯帮我拦下了不少因为坐标系、投影、裁剪边界引起的翻车。
做三级流域分析,最值钱的不是会调用某个接口,而是知道你手里的数据是用什么边界、什么投影、什么编码版本构成的。编码记住前四位是一二级,投影记住面积必须用等积,边界记住要融合不要硬切。按这三个习惯走,绕开上面五个坑,你的流域统计结果基本能过评审。
希望帮到你。如果碰到特别离谱的边界问题,不妨回到原始数据,把目标三级流域和相邻二级流域边界叠加在一起看,很多谜题就解开了。
本文还有配套的精品资源,点击获取