简介:面向ArcGIS使用者及地理信息相关专业研究者的长江流域中游干流区间基础地理数据包,以shp矢量水系与dem90m栅格地形为核心,涵盖制作专题图所需的核心底图资源,可支撑流域可视化、地图制图及初步空间分析。包内共71个文件,既包括shp、dbf、prj、sbn等ArcGIS矢量图层常规配套文件,也含有adf、nit、dat等栅格数据格式,以及mxd工程模板和jpg、pdf、eps三种已出图成果,整体压缩包约28.47MB,目录已分层整理,便于按需取用。目前已有1663人学习下载,适合需要快速获取长江中游流域底图、以ArcGIS开展实习或项目制图的人员。mxd工程内已建立图层链接思路,具有一定GIS基础的使用者可按常规流程关联解压后的矢量与栅格文件,即可输出设定区域的地形水系复合图;若完全不需要自行调整,也可直接使用附带的图片成果,省去制图操作。
1. 把长江干流中游区间的水系 shp 拖进 ArcGIS 之前,先分清这三层数据
“长江流域干流中游区间”不是行政区边界,而是长江干流宜昌至湖口之间、以干流为轴的两侧汇水范围。这类资料解压后通常不是一个 shp,而是水系线、子流域面、水文站点和地形图栅格/等高线的组合。很多人拿到手就拖进 ArcMap 或 ArcGIS Pro,结果看到河流断线、流域面跨出区间范围、shp 缺 .prj 文件、面积是负值——前 10 分钟不处理坐标系和拓扑,后面的裁剪、叠加、出图全都要返工。这篇按我处理中游水系数据时的顺序来写:先把 shp 的坐标系和拓扑问题清掉,再做区间裁剪和地形图套合,最后落到属性导出和脚本化,每一步都给到可以直接抄的参数和代码。
2. 坐标系统一:长江流域干流中游区间的 shp 投影选择
2.1 先检查 .prj,别让水系 shp 在经纬度下硬算长度面积
打开 ArcGIS 后第一件事不是看图层好不好看,而是右键图层打开属性,切到 Source 选项卡,看那个 shp 的 Spatial Reference 到底是什么。我经手的中游水系数据,相当一部分直接丢过来一个没有 .prj 的文件,ArcGIS 默认按未知坐标系加载,叠加 DEM 或影像时要么提示“Unreferenced data”,要么要素全部挤到西非沿海。
没有 .prj 时,先用 Define Projection 指定正确的坐标系;原来自带 .prj 但后续工具要求投影坐标时,用 Project 转坐标。长江流域干流中游区间的经度范围大约在 106°E 到 118°E 之间,按 3 度分带,38 带中央经线 114°E、39 带中央经线 117°E 都会跨到。做全区间统一分析时,我更倾向于用 CGCS2000 / 3-degree Gauss-Kruger CM 114E(EPSG 4549),这样一个投影覆盖整个中游,不用在分带边界裁来裁去。
import arcpy arcpy.env.workspace = r"D:\yangtze_mid\data" for shp in arcpy.ListFeatureClasses("*.shp"): out_shp = shp.replace(".shp", "_prj.shp") arcpy.Project_management( in_dataset=shp, out_dataset=out_shp, out_coor_system=arcpy.SpatialReference(4549) )这段代码会把工作空间下所有 shp 投影到 CGCS2000 3 度带 114E。注意arcpy.SpatialReference(4549)这里用的是 EPSG 代码,ArcMap 10.4 以上和 ArcGIS Pro 都认识;如果你手头数据还是西安 80 或北京 54 的老成果,不要直接 Project 到 CGCS2000,需要先找控制点做七参数转换,否则河流整体偏出去几十米。
2.2 流域面面积是负值:先 Repair Geometry,别急着重算
热搜里“arcgis 里面积是负的怎么处理”对应的就是 shp 面要素的 ring 方向问题。shapefile 的规则是外环逆时针、内环顺时针,一旦数据生产时把环的方向写反,ArcGIS 计算出的 Shape_Area 就可能是负数,或者面在显示时出现“空洞”和“交叉”两种诡异效果。
处理方法很直接:ArcToolbox > Data Management Tools > Features > Repair Geometry,把输入 shp 选进去,Geometry Type 选 ALL,按下确定即可。这个工具会重排环方向、删除零长度线段、修复自相交。我一般还会配合 Check Geometry 先跑一遍,把问题要素挑出来,Repair 完再 Check 一次,确认返回的 Errors 为 0。
| 工具 | 位置 | 解决什么问题 | 关键参数 |
|---|---|---|---|
| Define Projection | Data Management Tools > Projections and Transformations | 给无坐标信息 shp 赋予坐标系 | 只写坐标系,不改坐标值 |
| Project | 同目录 | 重投影到目标坐标系 | Geographic Transformation 可留空或指定 |
| Check Geometry | Data Management Tools > Features | 检查几何错误类型 | OID 和错误类型输出表 |
| Repair Geometry | 同上 | 修复坏几何 | Geometry Type 常用 ALL |
| Add Geometry Attributes | Data Management Tools > Features | 补算面积/长度等属性 | 支持 GEODESIC 测量 |
这里有个容易被忽略的点:Repair Geometry 会改变要素结构,修复前最好备份原始 shp。特别是后续要对接水文模型时,上游单位可能只认原始要素编码,一旦几何重建导致某个子流域面被拆成两个多部件,编码就断了。
2.3 水系拓扑检查:悬挂点、伪节点、自相交分开看
水系线 shp 的拓扑问题比面文件更多,而且表现隐蔽。常见三类:一是悬挂点,即河流线的一端没有连到任何其他线;二是伪节点,两条线在同一个点相交,但实际上是同一条河,只是被数据生产切成两段;三是自相交,一条折线绕回来跟自身交叉,常见于湖泊出口和弯曲河段。
ArcGIS 的拓扑检查必须把数据放进地理数据库。操作顺序:在 Catalog 里新建一个 File Geodatabase,把 shp 导入要素数据集,然后右键数据集新建拓扑。规则按中游水系的情况选下面几种,逐一说明:
| 拓扑规则 | 冲突含义 | 中游数据适用场景 |
|---|---|---|
| Must Not Have Dangles | 线端点必须被其他要素覆盖 | 查找断头支流;但区间出口处悬挂点要保留 |
| Must Not Have Pseudonodes | 线不能有伪节点 | 同一河流被人为拆分,需合并 |
| Must Not Self-Overlap | 线不能自重叠 | 重复采集或多次拼接产生 |
| Must Not Self-Intersect | 线不能自相交 | 河网扭曲时常见 |
| Must Be Single Part | 要素不能为多部件 | 用于面状湖泊被错误聚合 |
拓扑验证完成后,错误会列在 Error Inspector 里。修悬挂点时注意边界条件:长江干流流出中游区间的那个点,以及区间内支流汇入干流的点,本来就会出现单线端点,这两个位置的悬挂点不能删。真正要删的是中间悬空的短线头,多半是数据拼接时的残余线段。伪节点则用“Merge”把相邻两条线合并成一条,保留属性以较长线为准。
3. 用中游区间边界裁剪水系与流域 shp:Clip 和 Dissolve 的参数细节
3.1 Clip 裁剪水系线:XY Tolerance 别随手填
ArcToolbox > Analysis Tools > Extract > Clip 是最常用的裁剪工具。输入要素选中游区间的水系线或者流域面,裁剪要素选中游区间边界面,输出要素类可以直接写成 gdb 里的要素类,也可以写 .shp。输出写 .shp 时字段名会被截断为 10 个字符,中文字段名还会有变成问号的风险,所以能写进 GDB 就写进 GDB。
Clip 工具的 XY Tolerance 参数默认是空,工具会自动按输出坐标系的最小分辨率处理。中游水体资料常见的问题是这个容差过大,比如默认 0.001 度,在 114E 这个纬度下约等于 100 米,结果干流两岸的小支流被整体吸附到裁剪边界上,河流出口位置肉眼可见地偏了。我一般手动填 0.001 米,或者干脆填 0。
arcpy.Clip_analysis( in_features=r"D:\yangtze_mid\data\river_prj.shp", clip_features=r"D:\yangtze_mid\data\middle_basin_prj.shp", out_feature_class=r"D:\yangtze_mid\data\river_clip.shp", cluster_tolerance="0.001 Meters" )这里cluster_tolerance用的是字符串加单位,ArcGIS 也接受纯数字。裁剪结果的属性表完整保留原字段,但 Shape_Length 不会自动重新计算,需要后面用 Add Geometry Attributes 补。Clip 对线要素会沿裁剪边界切开,落在边界外的部分不输出,落在边界内的线段保留原几何,这跟 Intersect 不一样——Intersect 会把线与面相交处的节点重新生成,更适合后续做水文拓扑,但速度慢得多。
3.2 Dissolve 合并子流域,再 Erase 扣除上游下游
“长江流域干流中游区间”的数据往往被打包成整个长江流域的子流域面。比如数据源是按全国水系一级分区组织的,中游区间淹没在“长江流域”这个大面里。这时候先找到子流域面属性表里的分区字段,按它 Dissolve 合并出中游区间主体,再逐级扣除。
arcpy.Dissolve_management( in_features="subbasin_prj.shp", out_feature_class="middle_reach_raw.shp", dissolve_field="BASIN_CODE", statistics_fields="[['AREA_KM2', 'SUM']]", multi_part="MULTI_PART" )dissolve_field选的是分区编码字段,statistics_fields里的 SUM 会把所有同编码子流域面积加起来,方便跟官方控制断面面积对比。如果拿到的边界已经包含了整段中游区间,只缺局部支流,就用 Erase 工具把不需要的上游范围扣除掉。Erase 的输入是待处理面和擦除面,输出是去掉擦除部分后的剩余几何。操作上要注意:Erase 只做几何差,不感知流域拓扑,扣除完可能出现零碎小面,要用 Eliminate 把小于 1 平方公里的碎面合并到相邻大面里。
3.3 裁剪成果的 3 项质量检查
裁剪不是跑完就结束。我每次裁完中游水系,固定做三件事。第一,加载裁剪结果和原始 shp,目视检查干流出口、主要支流汇入点有没有被边界“吃”掉一小段。第二,用 Select By Location 从原始数据和裁剪结果中各统计一次要素数量,如果裁剪后数量骤减,多半是 Clip Features 本身有问题。第三,属性表中对流域面用 Shape_Area 重新排序,把面积最大的前 10 个面列出来,跟《长江流域防洪规划》里列出的主要子流域面积对一遍,误差超过 3% 就要回去查投影和容差。
这里有一个常见误用:有人为了“让裁出来的河网更平滑”,把 Clip 前的 XY Tolerance 设成 0.01 或更大。结果河网节点被大规模抽稀,原本弯曲的荆江段岸线变成直线段,流域面积损失几个百分点。裁剪时的容差只负责捕捉节点,不负责平滑,平滑要放到后续的 Simplify Line 里单独做。
4. 水系 shp 与地形图套合:DEM、等高线和影像校正
4.1 从 DEM 提取中游区间等高线,叠加前先把范围裁齐
“地形图”和“水系流域图”放在一个标题里,通常意味着最后要出一张带等高线的水文图。中游区间平原地带多,直接用全流域 DEM 提取等高线,山区的线会占满整个图幅,江汉平原上反而什么都看不到。我先用 Extract by Mask 把 DEM 裁到中游区间边界,再按等高距生成等高线,这样水系和地形才在同一个范围内说话。
from arcpy.sa import ExtractByMask, Contour dem_clip = ExtractByMask("dem_mid.tif", "middle_basin_prj.shp") contour = Contour( in_raster=dem_clip, out_polyline_features="contour_50m.shp", contour_interval=50, base_contour=0, z_factor=1 ) contour.save("contour_50m.shp")contour_interval在中游山区用 20 米或 50 米,平原区用 10 米或 5 米;base_contour设 0 表示从 0 米起算,实际使用时可以设成 20,让等高线数值落在整百上;z_factor是垂直比例因子,中游 DEM 如果是米制单位,填 1 即可。如果 DEM 是高程异常基准,比如以海图深度基准面起算,要先转成 1985 国家高程基准,不然等高线跟水位站数据对不上。
4.2 用天地图影像检查岸线偏移,再决定要不要 Spatial Adjustment
把水系 shp 和地形图套合后,另一个高频问题是岸线偏移。中游河湖岸线受冲淤变化影响,几年就变一次,你拿到的 shp 可能是十年前解译的成果,而底图影像是最新的,两者看起来就错开几十米。
在 ArcGIS Pro 里加载地图影像的标准操作是:地图选项卡 > Add Data > Data From Path,输入天地图服务地址;ArcMap 则在 Catalog > GIS Servers > Add WMS Server 里配置。加载后目视检查干流岸线是否骑在影像岸线上。如果偏差普遍在一个方向,先不要急着用 Spatial Adjustment 拉,去查一下 shp 的坐标系是不是被 Define Projection 强行标成了 CGCS2000,而真实数据还是 WGS84,两者差几十米很正常。把 .prj 信息改对,偏移就会消失。确认坐标系没问题之后,偏差依然超过 5 个像素再考虑配准:选 8~12 个均匀分布的桥梁、闸口、固定弯道控制点,RMS 控制在 0.5 以下,用橡皮页变换校正。
橡皮页变换会改线要素的几何形状,操作前必须在原始数据上复制一份。另外,只为出图而校正的话,建议在 ArcGIS Pro 里对图层设置“几何偏移”或者使用地理配准会话,不要直接覆盖源 shp,否则下次数据处理还要再回归一遍。
4.3 水系分级符号化:一组可以直接抄的参数
中游水系图里最常见的渲染错误是所有河流一个宽度、一个颜色,干流和断头渠分不出来。我的设置是:长江干流单独用一个图层,线宽 1.6 磅,颜色 RGB(31, 78, 121);一级支流(清江、汉江、洞庭湖水系主河道)0.8 磅,RGB(56, 110, 170);二级以下支流 0.3 磅,RGB(102, 152, 189)。流域面用分级色彩,按高程区间分 5 级,透明度 20%,这样底面等高线还能透出来。河流注记只标注“长江”“清江”“汉江”这些一级名称,次要支流标注留到大比例尺出图再加,否则图面全被文字占满。
5. 中游水系 shp 属性表加工:流域编码、长度修正与 shp 转 txt
5.1 用字段计算器批量补流域编码
拿到的中游水系 shp 属性表里,常见状况是有一个中文名称字段,但没有流域编码字段。后续入库或者对接模型时,没有编码就没法 join 水文站数据。最快的方法是字段计算器里用 Python 解析名称字段,批量写编码。
def zone_code(name): if name.startswith("清江"): return "CJZJ-QJ" if name.startswith("汉江"): return "CJZJ-HJ" if name.startswith("洞庭"): return "CJZJ-DT" return "CJZJ-OTHER"代码里的CJZJ前缀表示长江中游区间,后面接拼音缩写。字段计算器选 Python 解析器后,把这段函数粘贴到预逻辑脚本代码框,然后在下面一行输入zone_code(!NAME!),确定后完成赋值。需要注意:shapefile 的字段名不区分大小写但会被截断,如果你在 ArcMap 里用!NAME!拿字段,字段名必须是属性表里实际存在的名字。
5.2 用 Add Geometry Attributes 修正裁剪后长度面积
前面裁完的河流线,Shape_Length 还是原始值。ArcGIS Pro 2.2 以后的 Add Geometry Attributes 工具支持 GEODESIC 测量,比老式 Calculate Geometry 更适合跨投影带的中游数据。
arcpy.management.AddGeometryAttributes( in_features="river_clip.shp", geometry_property="LENGTH_GEODESIC", length_unit="METERS" )LENGTH_GEODESIC会在输出属性表里生成一条LENGTH_GEO字段,用来替代被投影变形扭曲的 Shape_Length。面要素同理,可以用AREA_GEODESIC生成以大圆面积计算的字段。如果工具版本不支持 GEODESIC,至少先用 Project 转到 4549 再计算,并且不要在经纬度图层上直接算。
5.3 shp 转 txt:导出给外业或模型用的标准写法
“shp 转 txt”在热搜里很常见,但 ArcGIS 自带的 Table to Text 工具导出的是带表头信息的长格式文本,模型程序读起来并不方便。更常见做法是用 ArcPy 的 SearchCursor 把需要的字段写成一行一行的 txt。重点是中文用 utf-8-sig 编码,否则在记事本和 Excel 里打开都是一堆乱码。
with open(r"river_attr.txt", "w", encoding="utf-8-sig") as f: cursor = arcpy.da.SearchCursor( "river_clip.shp", ["Code", "Name", "LENGTH_GEO", "SHAPE@XY"] ) for code, name, length, xy in cursor: f.write(f"{code},{name},{length:.2f},{xy[0]:.3f},{xy[1]:.3f}\n")SHAPE@XY返回的是点要素的代表点坐标,对线要素则取线的起点,所以导出时如果要知道河流入口位置,用SHAPE@STARTX和SHAPE@STARTY更准确。复杂面要素不要直接依赖SHAPE@XY,它只给一个点;要把洞、多环结构写全,需要遍历SHAPE@的getPart()方法,否则湖泊中间的岛会被丢掉。
5.4 批处理导出时容易踩的空值坑
光标遍历时,字段值为 None 是正常的,不能用空字符串拼接。导出前先统计属性表里哪些要素的关键字段是 Null,能补则补,补不了就统一写成NODATA。另一个坑是坐标小数位,导出时如果直接str(xy[0]),默认可能输出 17 位小数,txt 文件体积爆炸,用:.3f固定到三位数,对大多数水文分析精度足够;需要保留更高精度时再改成:.6f。
6. 把整套处理流程固化成一个 ArcGIS 脚本工具再交付
6.1 用参数承接中游区间面,而不是改脚本常量
前面每一节都可以独立跑,但在项目交付时,我习惯把“投影 → 拓扑修复 → 裁剪 → 计算长度 → 导出 txt”合成一个脚本,并用 ArcGIS Pro 的 Add Script 功能包成工具。这样换一个区间边界、换一套水系 shp,只需要改参数,不用打开代码编辑器。
import arcpy arcpy.env.overwriteOutput = True arcpy.env.outputCoordinateSystem = arcpy.SpatialReference(4549) in_shp = arcpy.GetParameterAsText(0) # 输入水系 shp mask_shp = arcpy.GetParameterAsText(1) # 中游区间边界 out_txt = arcpy.GetParameterAsText(2) # 导出 txt 路径 arcpy.Project_management(in_shp, "river_prj", arcpy.SpatialReference(4549)) arcpy.Clip_analysis("river_prj", mask_shp, "river_clip") arcpy.management.AddGeometryAttributes("river_clip", "LENGTH_GEODESIC", "METERS") with open(out_txt, "w", encoding="utf-8-sig") as f: cur = arcpy.da.SearchCursor("river_clip", ["Code", "Name", "LENGTH_GEO"]) for code, name, length in cur: f.write(f"{code},{name},{length:.2f}\n")arcpy.env.overwriteOutput = True让重复运行不会报文件已存在;outputCoordinateSystem设置成 4549 后,Clip 中间产物的坐标系会自动对齐,投影步骤甚至可以省略一部分。这里最能省时间的是把“拓扑修复”排除在脚本之外,因为自动修复会误删区间出口悬挂点,人工判断还是必要的,脚本只处理确定性最高的投影和裁剪。
交付后唯一要叮嘱使用者的是:环境设置里那行outputCoordinateSystem = 4549不要删。中游的干流和湖泊数据如果纬度跨了 3 度分带,后期接 Bellhsh 流方向计算或坡度分析时,不同投影会导致流域汇流路径偏移,到时候再返工重投比第一遍做慢得多。
本文还有配套的精品资源,点击获取