简介:面向 GIS 与水文水资源领域的从业者、研究者及高校相关专业师生,这套全国五级水系矢量数据集覆盖 R4、R5、R12、R12P 与三级流域共五套独立图层,可直接加载至 ArcGIS、QGIS 等主流 GIS 平台,服务于流域划分、水文建模、水资源统计、生态区划与防汛风险评估等空间分析任务。压缩包共 53 个文件,大小约 22.87MB,以 shp 矢量几何、dbf 属性表与 prj 坐标定义为主体,配套 shx、sbn、sbx 空间索引及 xml 元数据,同时附三级流域 JPEG 预览图与 RAR 备份压缩包,便于快速核验数据完整性和可视化效果。各图层坐标定义覆盖 WGS84 与 CGCS2000 两种常用地理坐标系,属性结构完整,可直接作为全国河网分级、流域边界提取及多尺度水文分析的基础底图,省去自行拼接与坐标转换环节。目前已有 77 人浏览学习,适合需要标准化水系分级矢量数据进行规划、科研与教学场景的数据使用者。
1. 五级水系SHP是什么:拿到数据后第一件事该做什么
做水利、环保、规划或者农业项目的朋友,应该都遇到过同一个问题:需要一张能看清「从大江大河到田间小沟」的完整流域图。市面上能下载到的大多是线状河网,或者只有省界和国界,真正带层级关系的流域边界非常难找。中国五级水系矢量数据集的价值就在这:它把全国流域按一级到五级逐层切开,每一级都有对应的面状边界文件,打开就能直接用于空间分析,不用再从DEM里自己提一次河网。适合谁用?做洪水淹没分析、排污口关联、水土保持区划的工程师,以及需要按流域汇总统计的研究人员。拿到数据的第一件事,不是急着画图,而是先把文件结构、坐标系和字段含义摸清楚。
2. 从一级到五级:流域层级逻辑与数据集结构
2.1 五级流域的划分逻辑:从大江大河到小支流
中国的水系划分不是简单按河流长度排的,而是按「汇水区域」来切。一级流域通常对应全国尺度的大江大河干流及其主要支流汇水区,比如长江流域、黄河流域这样的大块;二级流域在一级内部按重要支流或者水文分区再切;三级、四级继续往下细分;到了五级,基本就是县域范围内的小支流、山溪沟道的汇水单元。每一级都是上一级的严格子集,画到地图上就像一套嵌套的「套娃」。
这套层级关系是分析的基础。比如你在做某个水源地的风险评估,如果用一级流域做统计,整个长江流域都会被算进去,结果没有意义;用五级流域做统计,才能落到具体的某几条小支流上。所以真正干活的时候,绝大多数业务场景至少要用三级或四级,精确一点的直接上五级。这也是为什么这个数据集把五级边界全部拆开放进SHP里,而不是只给一个总的缩略图。
有个容易混淆的点需要先澄清:很多数据包里会同时出现「河网线」和「流域面」两种SHP。河网线描述水流的路径和拓扑关系,流域面描述水流汇集的区域范围。五级水系矢量数据集的核心是面状的流域边界,线状河网通常是辅助用的。分析的时候,统计类任务以面为主,路径类任务以线为主,两者不要搞混。
2.2 一套SHP文件夹里通常装了哪些内容
下载解压之后,你会看到一个文件夹里有大量同名但后缀不同的文件。SHP不是单文件格式,一个完整的Shapefile必须包含至少三个文件:.shp是几何信息,.shx是索引,.dbf是属性表。完整一点还会有.prj(坐标系)、.cpg(编码)、.qix(空间索引)。常见机构发布的数据还会配一个说明文档或者元数据表,里面写清楚投影、比例尺和数据来源。
按级别拆分的文件组织方式通常是这样的:一级流域一般是1到2个SHP文件,二级是几十个,到了五级可能是上万个单元,一般会按流域编码前缀拆成多个SHP分片存放。命名规律通常是「级别编码+流域代码」,比如某个二级流域的文件名里带它的流域编码前缀,这样在程序里批量读取时很容易按前缀分组。
属性表里至少会有几个关键字段:流域编码、流域名称、级别、面积、上下游关系或者父级编码。流域编码是最重要的字段,它在整个数据集里唯一标识一个流域单元,后续做空间关联、数据汇总都要靠它。有些数据源还会附带河网分级字段和出水口坐标,用来做水文分析。
2.3 为什么不直接用在线地图瓦片或DEM
有人会问:现在在线地图随便拖一拖就能看到河网,为什么还要用SHP数据集?原因很简单:在线地图是栅格瓦片,本质是图片,拿不到矢量边界,也没法做空间查询和面积计算。另一个常见替代方案是用DEM自己提取流域边界,比如用SWAT或者ArcGIS的水文工具,理论上能算出坡面汇流和流域分水岭,但DEM提取的结果受分辨率影响很大,30米和90米提出来的边界能差出好几百米,而且需要手动设置汇流累积量的阈值,这个值调起来很玄学,不同区域要试很多次才稳定。
SHP数据集的优势在于它是经过人工修正和统一编码的成果。五级流域边界是基于高精度地形和河网资料综合生成的,边界已经平滑处理过,拓扑关系基本是闭合的,拿到手直接能用。当然也有代价:数据是静态的,一条新修的灌溉渠或者城市河道改造不会反映在里面,所以做小尺度项目时,最好用SHP做底图,再用高分辨率影像或者实测数据局部修正。
3. 在QGIS和Python里读取筛选:两种上手最快的路径
3.1 用QGIS打开五级水系SHP:正确设置坐标系
打开SHP表面上看是「拖进去就行」,但坐标系如果不对,后面所有分析都会偏。QGIS打开文件后,第一步看右下角显示的CRS,再右键图层看属性,确认坐标系是什么。中国数据常见的坐标系有两种:CGCS2000(国家大地坐标系,平面投影)和WGS84经纬度。如果是CGCS2000且带投影带号,直接保留,不需要重投影;如果是WGS84经纬度,做距离和面积计算前要转成投影坐标系。
一个大多数新手会踩的坑:图层显示位置没问题,但一旦用「计算几何」或者「缓冲区」工具,结果单位对不上。比如经纬度坐标系下的缓冲区单位是度,不是米,填个500出来的是一大片。所以进项目的第一步是按表检查:
| 项目 | 检查方式 | 正确的做法 |
|---|---|---|
| CRS定义 | 图层属性 | 有.prj文件且内容可读,直接信任 |
| 几何范围 | 缩放到图层 | 中国范围内的经纬度约73-135°E,18-54°N |
| 面积字段 | 查看属性表 | 确认单位是m²、km²还是公顷 |
| 几何类型 | 图层属性 | 流域边界必须是Polygon,不能是Polyline |
如果打开后发现图形呈扁平状或者无法显示,大概率是.prj文件缺失,QGIS按未知CRS处理了。这时候不要乱猜坐标系,先看数据发布说明,找不到说明就用「中国范围内经纬度」这个范围值反推,或者直接把经纬度范围对一下再手动指定。
3.2 按流域级别筛选:表达式与导出子集
五级水系数据集很多时候不需要全部加载,做区域分析时只要抽其中某几级,或者按流域编码前缀筛选。QGIS里最常用的方式是按属性选择。
"LEVEL" = 5这个表达式选出所有五级流域单元。如果字段名是中文,比如「级别」,需要写成"级别" = 5,注意中文双引号不能省。如果只想取某个二级流域下所有的三级到五级单元,用代码前缀筛选:
"CODE" LIKE '04%' AND "LEVEL" >= 3这段的逻辑是:流域编码按层级递增,比如「04」是二级流域编码,所有以04开头的编码都是它的下级,LIKE '04%'做了前缀匹配,加上级别限制后精准过滤。筛选完成后右键图层选择「导出-保存要素」,导出时务必勾选「保存为CRS」,把坐标系保持原样,别让QGIS自动转成WGS84,因为后面做面积计算时投影坐标系更好用。
表达式还有一个实用技巧:按名称模糊匹配。比如需要提取所有名字里带「水库」或「灌区」的单元,用"NAME" LIKE '%水库%'。注意中文字段和中文值在部分版本里要确保编码设置正确,否则匹配不到,这个在第5章避坑里细说。
3.3 用GeoPandas批量统计各级别单元数量
如果数据分片特别多,或者要用脚本批量处理,直接用Python的GeoPandas读取比QGIS手动操作更高效。数据处理前置条件:安装geopandas和pyogrio引擎,读取SHP的时候建议显式指定引擎。
import geopandas as gpd gdf = gpd.read_file("watershed_l5.shp", engine="pyogrio") print(gdf.columns) # 查看字段 print(gdf.crs) # 查看坐标系 print(gdf.geom_type.unique()) # 几何类型检查 # 按级别统计数量 count_by_level = gdf.groupby("LEVEL").size() print(count_by_level)读取时的engine="pyogrio"是加速选项,pyogrio是矢量IO的C实现,比默认的fiona快很多。gdf.crs输出形如EPSG:4490,这是CGCS2000的经纬度表述;如果显示None,说明.prj缺失,需要手动赋值:
from pyproj import CRS gdf = gdf.set_crs("EPSG:4490", allow_override=True)需要注意set_crs只改元数据、不改坐标值,只有当你知道原始坐标确实是经纬度时才用这个方法。如果数据本身是投影平面坐标,直接赋CGCS2000经纬度会整个偏掉。
统计完数量后,还能顺便检查数据完整性。中国五级流域的单元总量巨大(常见数据在数万个到十万级之间),如果统计结果只有几百个,说明分片没读全,或者过滤器被某个无效几何卡住。这时候用is_valid批量检查:
invalid = gdf[~gdf.geometry.is_valid] print(f"invalid geometry count: {len(invalid)}")结果里的无效几何常见的表现是自相交或环断裂,这类单元在空间分析时会报错,建议直接剔除或做缓冲区修复。
4. 落库到业务:格式转换、裁剪与空间关联
4.1 转成GeoJSON给前后端
五级流域数据最常见的下游去向是两个:一是给WebGIS前端做可视化,二是给数据中台做离线分析。前端一般不吃SHP,统一转成GeoJSON或者PMTiles。GeoJSON是明文的,浏览器直接解析,不用额外写解析器。
gdf_wgs84 = gdf.to_crs(epsg=4326) gdf_wgs84.to_file("watershed_l5.geojson", driver="GeoJSON")转换时的关键参数是坐标系。前端地图底图通常是Web墨卡托(EPSG:3857),但GeoJSON标准要求用WGS84经纬度,前端拿到后再由地图引擎自动投影,所以这里必须先转成EPSG:4326再导出。如果不转,几何数据可能带着原有的投影坐标,前端画出来要么是空的要么位置错乱。
需要留意的是GeoJSON的体积。五级流域全量转出来通常有几百兆,浏览器加载会卡到怀疑人生。常见的处理策略是把数据按级别拆分,单独输出五级或四级即可,或者用支持矢量瓦片的工具先把GeoJSON转成.pmtiles格式,按瓦片切片加载。转瓦片前先用gdf.geometry.simplify(0.001)做一下抽稀,保留精度和体积的平衡,阈值按原始坐标单位定,经纬度坐标下0.001度大约100米左右。
4.2 按行政区或项目范围裁剪
做实际业务时很少直接用全国数据,通常是划一个项目范围——比如某块工业园区或者某个流域的局部——把五级边界裁剪出来,减少数据量也聚焦范围。
from shapely.geometry import box # 项目范围投影坐标:东侧、南侧、西侧、北侧 bbox = box(32000000, 3400000, 3220000, 3600000) # 仅为示例数值 clip_gdf = gdf.to_crs("EPSG:3857") clipped = gpd.clip(clip_gdf, bbox) print(clipped.shape)这段代码先定义了一个矩形范围,再to_crs把流域数据转到和项目范围一致的投影,然后clip裁剪。核心参数是那个方括号坐标范围,按你项目实际的投影坐标填。注意如果gdf和bbox坐标系不一致,gpd.clip的结果往往是空,这是最常见的翻车点。
裁剪完成后有两个后续动作建议做:第一,导出时删掉面积小于某个阈值的碎块,裁剪产生的边缘小块没有分析价值;第二,对被边界切开的流域单元做面积重新计算,不要沿用原始SHP里那个总面积字段,因为它现在已经是裁剪后的部分流域。
4.3 把监测站点关联到流域字段
排污口、水质监测站、雨量站,这类点状数据要落到流域上,在GIS里叫「点面关联」。GeoPandas里的空间连接直接做:
stations = gpd.read_file("stations.shp") # 点数据 joined = gpd.sjoin(stations, gdf, how="left", predicate="within") print(joined[["site_name", "CODE", "LEVEL", "NAME"]].head())sjoin的predicate="within"表示点在流域面内部才算匹配;how="left"保证左侧站点全部保留,没匹配上的会得到空值。参数"within"和"intersects"的区别要弄清楚:intersects 对落在边界线上的点也能匹配,而流域间边界是共享的,同一条线上的点可能同时匹配两个流域,业务上就会产生重复归属。我的做法是优先用 within,若确有站点悬在边界线上,再用 intersects 并用面积较大的流域兜底。
还有一个常见隐含坑:SHP里的点坐标和面坐标如果来自不同坐标系但元数据都是WGS84,实际可能偏差几十米。关联前先叠加做目测抽查,确认站点落位和山脉、河流走向一致,避免出现站点落在山脊上的低级错误。
5. 避坑笔记:五级水系SHP最常见的五个坑
5.1 拷贝后打不开:SHP是「一拖多」文件
现象:把某个SHP拷贝到同事电脑后,打开提示「缺少.shx或.dbf文件」,几何显示为空。原因:SHP不是一个文件,而是一组同名文件,拷贝时只复制了.shp,其他兄弟文件没跟上。解决:拷贝时连同整个文件夹一起拷,或者手动确认至少包含.shp、.shx、.dbf、.prj四个文件。我自己习惯把所有SHP放进同一个目录后压缩成zip再发,避免漏文件。
5.2 中文乱码:属性表里全是问号
现象:属性表里流域名称、地名字段显示成乱码,筛选中文时匹配不到。原因:.dbf的编码声明在.cpg文件里,老数据经常缺失或者写的是GBK、UTF-8混乱。解决:QGIS里调整数据源编码为UTF-8或GB18030;GeoPandas读取时指定编码:
gdf = gpd.read_file("watershed.shp", encoding="GB18030", engine="pyogrio")encoding参数在不同引擎下的表现不一样,pyogrio默认按.cpg文件识别,遇到缺失时我一般直接用fiona引擎试试,再不行就把数据先转成GeoJSON,在转换过程中修正字符串编码。
5.3 面积字段的单位被搞混
现象:统计某五级流域面积,代码算出来和属性表里的面积字段差100万倍。原因:属性表的面积字段常见有三种单位:平方米、公顷、平方公里。同一份数据里有时还会混合出现。解决:先看元数据或字段注释,没有就抽一个流域用计算几何验证量级。计算时养成习惯,输出结果统一转成平方公里:
gdf["area_km2"] = gdf.geometry.area / 1_000_000如果坐标系是经纬度,geometry.area的单位是度²,不能用来算实际面积,必须先to_crs投影,再算面积。
5.4 坐标系缺失导致整体偏移
现象:图层显示的位置在非洲西海岸或者一片空白,和底图对不上。原因:.prj文件丢失,软件默认按WGS84处理,而数据实际是CGCS2000或西安80坐标。解决:先看数据发布说明里的坐标系描述,确认原始投影后手动设置。
gdf = gdf.set_crs("EPSG:4490", allow_override=True) # 假设原始是CGCS2000经纬度这能修正元数据。但如果原始数据不是经纬度而是高斯投影,手动赋经纬度依然没用,这时得用to_crs做强制转换,或者回到GIS桌面上用「定义投影」工具加载原有的投影参数来修复。
5.5 相邻流域之间的缝隙与重叠
现象:相邻流域边界之间隐约有条细缝,叠加栅格数据时缝隙处没值;有的地方则两个流域重叠,面积统计时会重复计算。原因:多源数据拼接时没有做拓扑检查,原数据集在拼接缝上做了简化。解决:先检测缝隙和重叠:
overlay = gpd.overlay(gdf, gdf, how="intersection") # 筛选重叠部分面积大于零的记录检测结果里大量重叠说明接边有问题,常见解决方法是按共同边界聚类并做简单的缓冲区清理。注意处理重叠要保留一个优先级规则,比如按级别高的优先,不能一刀切删除。对于面之间的细微缝隙,后续做栅格分析时通过「扩大范围再裁剪」的办法消除影响,不要执着于把边界焊死。
6. 进阶技巧:用DEM提取河网,反向校验这套边界
五级流域边界是成品数据,但它到底准不准?最直接的验证方法是拿起一个区域的DEM,自己从地形里提一套河网出来,和SHP里的边界做对照。婆验的基准不是「完全一致」,而是「主干河道的流向和边界走向一致」。
from pysheds.grid import Grid grid = Grid.from_raster("dem30m.tif") grid.fill_depressions() flood = grid.flowdir() acc = grid.accumulation(flood) # 提取累积量大于1000的河网 streams = acc > 1000这里面有两个关键参数:fill_depressions填洼,把地形里的坑填平让水流能顺畅往下走;累积量阈值1000是关键,这个值越小河网越密、毛刺越多,越大越稀疏。我的经验是山区用500-1000,平原用3000-5000,因为平原地区地形平坦,算法容易产生假河道,必须用更大的累积量过滤噪声。这组参数在不同数据源上差异很大,初次跑时多试几个阈值,和SHP里的实际河网对比密度再确定。
提取完河网后,把栅格河网转成矢量线,再叠加到五级流域边界上。验证内容重点看几处:主流是否沿着上下游边界走、是否横穿分水岭。如果河网大面积横穿边界,说明该区域的DEM和SHP精度不一致,需要进一步核实到底是哪份数据有问题。曾经有个项目,我用30米DEM提河网后发现局部边界偏移了大概两三百米,经过比对发现是SHP里的某条三级流域边界沿用了旧资料,而DEM反映的是更真实的汇水地形,最后以DEM修正后的边界为准。
更进一步的用法是提取每个五级流域的「理论出水口」。方法是把一个流域边界作为掩膜,计算掩膜范围内累积量最大的栅格位置,那就是流域出口。把理论出口和SHP里标注的出水口位置做个距离对比,距离超过1公里的流域单元要重点检查——往往存在边界划分不合理或者DEM地形突变的问题。这套方法做下来,大概能排查出5%左右的问题单元,对这些单元单独修正,就能保证全库质量。
数据质量这种事说到底没有百分之百完美,成品SHP省了时间,但专业判断才是最后一道关。我现在拿到任何一份水系数据,都会先提一次DEM河网做抽查,形成习惯后省掉了很多后期返工的麻烦。希望帮到你。
本文还有配套的精品资源,点击获取