第14章 - 地形与水文分析

地形是所有”从地面往上算”的分析的底座:坡度、坡向、山体阴影是它的导数,视域是它的可见性,河网与流域是它的汇流结构。GeoLibre 把这一整条链路拆成两层——GeoLibre Toolbox → Raster 里那十几个向导式工具,与 Whitebox Toolbox 里上千条 WASM 工具中的 Terrain / Hydrology 两族。

本章基于 GeoLibre 3.4。地形与水文工具的参数名全部来自仓库内的 whitebox-catalog-snapshot.json(本机快照 775 条工具)与 packages/processing/src/raster-tools.ts;视域部分另外对照 packages/processing/src/terrain-viewshed.ts。菜单路径与控件名保留英文原文。

14.1 DEM 数据入口

14.1.1 六条入口

DEM(Digital Elevation Model,数字高程模型)在 GeoLibre 里不是一个特殊的图层类型——它就是一张单波段的栅格。所以”哪里能拿到 DEM”这个问题,答案等于”哪里能拿到栅格”。

入口 路径 覆盖面 是否需要 Key 适用
本地 GeoTIFF Add Data → Raster Layer 选文件 你自己手上的数据 否 精度最高,正式分析唯一推荐
本地 COG(URL) Add Data → Raster Layer 填 URL 任意可访问的 COG 否 大范围不进内存,HTTP Range 按需取块
Download Global DEM Processing → GeoLibre Toolbox → Raster → Download Global DEM 全球(AWS Terrain Tiles) 否 快速试手、教学、视域底图
STAC Layer Add Data → STAC Layer 任意 STAC 目录的栅格条目 视目录而定 按时间/范围检索,一次拿一景
Planetary Computer Processing → Planetary Computer Sentinel / Landsat / NAIP / DEM 等 否(服务端签名) 云目录浏览,点选加载
Earth Engine Processing → Earth Engine GEE 数据集 是(需认证) 需要用 GEE 的特定产品时

还有一类容易漏掉:地形底图。Controls → Terrain 打开的三维地形用的是 Terrarium 地形瓦片,它不是一个图层,但右键 Quick analysis → Viewshed from here 会自己去拉同一套瓦片——不需要你先加载 DEM,也不需要开着三维地形。

14.1.2 Download Global DEM 的确切参数

这个工具挂在 GeoLibre Toolbox → Raster 子菜单的最上面,但它打开的是 Whitebox Toolbox 对话框,工具 id 是 download_global_dem,参数只有两个:

参数名 类型 必填 默认 说明
bbox string 是 — WGS84 范围,格式 west,south,east,north
bbox_crs int 是 4326 范围所用的坐标系;只接受 4326

数据源是公开的 AWS Terrain Tiles,瓦片模板为:

https://s3.amazonaws.com/elevation-tiles-prod/geotiff/{z}/{x}/{y}.tif

实现里写死了四个限制,直接决定”能下多大一块地”:

常量 值 含义
TILE_SIZE 512 px 每块瓦片边长
MAX_ZOOM 12 最高只到 12 级
MAX_TILES 64 单次请求最多拼 64 块瓦片
WEB_MERCATOR_LIMIT 85.051129 纬度硬边界,超出直接拒绝

工具会从 12 级往下试,选第一个瓦片数不超过 64 的级别。所以框选范围越大,拿到的分辨率越粗——这不是 bug,是”浏览器内存安全”的设计。范围过大时会报 The selected extent is too large to download in the browser.

注意:这个工具不写文件,它把拼接裁剪后的栅格直接放进内存、加成地图图层。要留档,用图层的 Export → GeoTIFF (COG)(见 第06章 图层管理与地图控制)。

14.1.3 COG 为什么值得单独说

大范围 DEM 直接当普通 GeoTIFF 加载,会把整个文件读进内存。COG(Cloud-Optimized GeoTIFF)用 HTTP Range 请求按块读取,只取当前视口需要的部分。GeoLibre 的栅格渲染器、物方分析工作台(OBIA)、以及 Whitebox WASM runner 都能读 COG 的 URL。

实际差别:一个 2 GB 的 30 m 全国 DEM,当 COG 挂上去,第一次渲染只需要几百 KB;当普通 GeoTIFF 挂上去,先等两分钟下载完再看。

提示:自己转换用 Processing → GeoLibre Toolbox → Conversion → Raster to COG。它有客户端引擎,浏览器里也能跑,不需要 sidecar。

14.1.4 分辨率怎么选

分析目标 建议分辨率 理由
区域地貌、等高线总图 30 m(SRTM / ASTER / Copernicus DEM) 全国尺度可用,文件可控
小流域水文 10–30 m 河网密度与 DEM 分辨率直接相关,太粗会丢支流
场地级视域、坡度 1–5 m(LiDAR 派生) 微地形决定通视,30 m 会把山脊抹平
城市内涝、微汇水 ≤ 1 m 需要建筑与路面的高差

Whitebox 的 Hydrology 工具对分辨率很敏感:extract_streams 的 threshold 是汇流累积像元数,换分辨率必须重设阈值,不能沿用。

14.2 地形产品:山体阴影、坡度、坡向、曲率、粗糙度

14.2.1 三条实现路径

同一件事,GeoLibre 提供了三个入口,参数名和默认值并不完全一样:

路径 入口 引擎 输入形态
GeoLibre Raster 工具 Processing → GeoLibre Toolbox → Raster → Hillshade / Slope / Aspect 客户端(浏览器)+ sidecar 文件路径,或”用地图上的图层”
Whitebox 目录 Processing → Terrain → … 或 Whitebox Toolbox 搜索 WASM(默认)/ sidecar 图层或文件
Model Builder 节点 调色板里的 whitebox:slope 等 WASM 上游连线

前三个(Hillshade、Slope、Aspect)都有客户端引擎,所以纯浏览器版也能用,这是它们和 Contour / Clip by mask layer 的关键区别。

14.2.2 Hillshade:两个默认值不一样

hillshade 的成品是灰度晕渲图。两套实现的参数对比如下:

参数 GeoLibre Raster 工具默认 Whitebox hillshade 默认 含义
azimuth 315 315 光源方位角,0–360,西北向(315)是制图惯例
altitude 45 30 光源高度角,0–90
z_factor 1 1 高程放大系数

注意:altitude 的默认值差了 15°。45° 是”柔和、细节均匀”的取舍,30° 是”低角度、强调微地形”的取舍。做地形起伏夸张的晕渲选 30,做基础底图选 45。切换入口时不要以为参数被记住了。

光照方位角的惯例值得解释一句:315°(西北)配 45° 高度角能产生”山脊朝左上、阴影朝右下”的视觉效果,人眼习惯把这种图解读为凸起;反过来(光源在东南)同一份 DEM 会被看成凹陷。所以别把 azimuth 改成 135 然后抱怨地形反了。

出图组合:晕渲图放在彩色图层(如高程分色或坡度分色)下面,把上面那层的混合模式设成 Multiply。这比降低不透明度好——不透明度会让两个图层都发灰,Multiply 让颜色透出来而地形细节保留。

14.2.3 Slope:单位是必选项

slope 的参数只有三个:

参数 类型 默认 可选值
input Raster / str — 必填
units str degrees degrees / radians / percent
z_factor float 1 —

GeoLibre Raster 工具的 Slope 面板里同样是 Units(Degrees / Percent)与 Z factor 两项(Whitebox slope 的 units 还接受 radians,Raster 面板未暴露该选项;需要弧度时从 Whitebox 工具箱运行)。两种单位的换算关系不需要背:

percent = tan(degrees × π / 180) × 100
degrees = atan(percent / 100) × 180 / π

工程判断:坡度用于工程计算(土方、侵蚀、可建性)时用 percent,因为它们是线性可加的;用于制图或分级展示时用 degrees,因为人类对角度有直觉。45° = 100%。

14.2.4 Aspect:为什么它没有 z_factor

aspect 输出的是最陡下降方向的罗盘方位(0–360°,0/360 为北)。GeoLibre Raster 工具的 Aspect 参数列表是空的——源码注释直接给出了原因:

// Aspect is a direction, so a z_factor would have no effect on the result.
parameters: [],

这句话的推理值得记住:z_factor 是沿高程轴的线性缩放,它改变坡度的大小,但不改变坡面朝哪个方向倒。所以任何只关心”方向”的派生产物都不需要 z_factor。

Whitebox 目录里的 aspect 仍然保留了 z_factor 参数(默认 1),填别的值不会报错,但结果不会变。这是两套实现的一处不对称,不必当成 bug。

14.2.5 曲率族:一次搞清十几种

曲率描述”坡面弯曲的方式”,是水文与地貌分析里最容易被名字搞混的一族。Whitebox 把它们全放在 Terrain - Derivatives 分类下:

工具 id 显示名 回答的问题
plan_curvature Plan Curvature 垂直于最陡坡方向上的弯曲(影响水流发散/汇聚)
profile_curvature Profile Curvature 沿最陡坡方向上的弯曲(影响水流加速/减速)
mean_curvature Mean Curvature 两个主曲率的平均
gaussian_curvature Gaussian Curvature 两个主曲率的乘积,判断局部形状(峰/鞍/谷)
total_curvature Total Curvature 曲面的整体弯曲量
maximal_curvature Maximal Curvature 两个主曲率中较大者
minimal_curvature Minimal Curvature 两个主曲率中较小者
tangential_curvature Tangential Curvature 等值线方向上的曲率
casorati_curvature Casorati Curvature 对旋转不敏感的曲率度量
curvedness Curvedness 曲率的整体强度
shape_index Shape Index 把局部形状归到连续的形状谱上
ring_curvature Ring Curvature 环形邻域上的曲率
rotor Rotor 旋转分量
difference_curvature Difference Curvature 曲率差
accumulation_curvature Accumulation Curvature 汇流累积意义上的曲率
unsphericity Unsphericity 与球面的偏离程度
horizontal_excess_curvature / vertical_excess_curvature 水平/垂直超额曲率 —
principal_curvature_direction Principal Curvature Direction 主曲率方向(角度)

实际用得最多的只有四个:plan(水流发散)、profile(水流加速)、mean(总体凹凸)、gaussian(鞍部识别)。其余多为地貌学研究用。

plan_curvature 与 profile_curvature 的参数一致:input、z_factor(默认 1)、log_transform(默认 false)。

提示:曲率的量纲是 1/长度,数值极小(常见 ±0.001 量级)。直接做色带会全是中间色。要么开 log_transform,要么在 Style 面板里手工把色带的 min/max 设到实际数据范围的 5%–95% 分位。

14.2.6 粗糙度族

“粗糙度”不是单一指标,Whitebox 给了一组:

工具 id 显示名 度量
ruggedness_index Ruggedness Index 邻域内高程差的总和(地形起伏强度)
standard_deviation_of_slope Standard Deviation Of Slope 邻域内坡度的标准差
edge_density Edge Density 邻域内”边缘”像元占比
average_normal_vector_angular_deviation Average Normal Vector Angular Deviation 法向量角偏差的平均
spherical_std_dev_of_normals Spherical Std Dev Of Normals 法向量的球面标准差
circular_variance_of_aspect Circular Variance Of Aspect 坡向的圆方差(各向异性)

这一族里只有 ruggedness_index 没有可调窗口(input + output);standard_deviation_of_slope 带 filter_size(默认 11)与 z_factor(默认 1),edge_density、average_normal_vector_angular_deviation、spherical_std_dev_of_normals 也各有 filter_size,circular_variance_of_aspect 的窗口参数名为 filter(默认 11)。想控制尺度,改窗口或先重采样 DEM。

14.2.7 一条推荐的制图流水线

  1. Slope(degrees)→ 用色带渲染,作为主视觉。
  2. Hillshade(azimuth 315、altitude 45)→ 叠加在上一步下面,上面那层设 Multiply。
  3. Controls → Colorbar 打开色标,让读图的人知道颜色对应多少度。
  4. 需要更”专业”的观感时,改用 hypsometrically_tinted_hillshade(Hypsometrically Tinted Hillshade)——它把高程分色和晕渲一步合成,参数有 solar_altitude(默认 45)、hillshade_weight(默认 0.5)、brightness(默认 0.5)、atmospheric_effects(默认 0)、palette(默认 atlas)、reverse_palette、full_360_mode、z_factor。

14.3 等高线生成与标注

14.3.1 GeoLibre Raster → Contour

Processing → GeoLibre Toolbox → Raster → Contour。它没有客户端引擎,必须走 rasterio sidecar,也就是桌面版(或带 sidecar 的部署)。

参数 类型 必填 默认 说明
band number 否 1 读第几个波段
interval number 是 — 相邻等高线的高程差,最小 0.1
base number 否 0 起始高程,等高线落在 base 加 n 倍 interval 上
attribute string 否 elev 输出矢量里存高程值的字段名

输出是 GeoJSON 矢量图层(不是栅格),可以直接用图层菜单导出成 GeoPackage / Shapefile。

14.3.2 Whitebox → Contours From Raster

如果不想开 sidecar,Whitebox 目录里的 contours_from_raster 是 WASM 的,参数更多也更可调:

参数 类型 默认 说明
input Raster / str — DEM
contour_interval float 10 等高距
base_contour float 0 起始等高线高程
smoothing_filter_size int 9 生成前对 DEM 做平滑的窗口;调大则线更圆滑但丢细节
deflection_tolerance float 10 折线简化容差;调大则顶点更少、文件更小

两个实现在语义上一致(等高距 + 起始高程),差别是 Whitebox 版多了平滑与简化两个”出图友好”的旋钮。

14.3.3 等高距怎么定

等高距不是随便填的,它取决于 DEM 分辨率与出图比例尺:

DEM 分辨率 建议等高距 说明
90 m 50–100 m 间距小于 2 个像元的高差,生成的线会互相缠绕
30 m 10–20 m 通用地形图常用
10 m 5 m —
1 m 0.5–1 m 场地设计

一个经验法则:等高距不小于 DEM 垂直精度的 2 倍。SRTM 的垂直精度约 ±16 m,所以拿 SRTM 出 5 m 等高距是在画噪声。

14.3.4 标注

等高线标注不能在生成阶段做——Contour 工具只产出几何和高程字段。标注走图层样式:

  1. 选中等高线图层,打开 Style 面板。
  2. 添加一个基于 elev 字段的 label(符号标注)。
  3. 标注位置用 line placement(沿线放置),避免压在节点上。
  4. 只标计曲线(每 5 条加粗的那种):用 Select by value 或 SQL 过滤 elev 能被「interval 乘 5」整除的要素,另存一层专门标注。

注意:等高线密集区标注必然重叠。要么按比例尺分段出图(分幅),要么只在计曲线上标注。

14.4 视域与通视分析

14.4.1 右键快捷入口(最快的一条路)

右键地图 → Quick analysis → Viewshed from here 提供三个半径预设:2 km、5 km、15 km。

特性 行为
观察者高度 1.8 m(一个站立的人),加在点击点的地面高程上
高程来源 公开 Terrarium 地形瓦片,按需拉取点击点周围的方形区域
需要先加载 DEM 吗 不需要
需要打开三维地形吗 不需要
结果形态 一个图像叠加图层,名字形如 Viewshed (5 km)
图层能力 有透明度、可重排、可 zoom-to、可删除,随项目保存

结果是一个 RGBA 覆盖层:可见像元涂半透明绿(默认颜色 [56, 189, 108]、alpha 110),其余完全透明——这样底图影像还能看清。

14.4.2 底层是怎么算的

理解实现有助于判断结果可不可信。整条链路在 terrain-viewshed.ts 里。

第一步,拼 DEM。 从 Terrarium 瓦片解码高程:

export function decodeTerrariumElevation(r: number, g: number, b: number): number {
  return r * 256 + g + b / 256 - 32768;
}

Terrarium 把高程打包成「R 乘 256 加 G 加 B/256,再减 32768」,覆盖 −32768 到 +32768 m,精度 1/256 m。瓦片是 256 px,最高 15 级。

第二步,定瓦片级别。 目标是让分析方格边长约 512 像元(TARGET_GRID_SPAN):

export function zoomForRadius(radiusMeters: number, lat: number): number {
  const resolutionAtZoom = (z: number) =>
    (156543.03392 * Math.cos((lat * Math.PI) / 180)) / Math.pow(2, z);
  const wanted = (radiusMeters * 2) / TARGET_GRID_SPAN;
  for (let z = TERRARIUM_MAX_ZOOM; z >= 0; z -= 1) {
    if (resolutionAtZoom(z) >= wanted) return z;
  }
  return 0;
}

第三步,逐格走视线。 对每个目标格,从观察者到它走一条直线,记录沿途的最大坡度;目标格的坡度超过沿途所有坡度才算可见——这是标准的 R3 式视线算法。

复杂度是 O(n³)(n² 个格子,每条最多 n 步)。512 的边长意味着上亿次内层迭代,所以它必须在 Web Worker 里跑,否则标签页会卡死几秒。computeViewshedAsync 做的就是这件事,Worker 不可用时退化成同线程同步算。

边界常量:

常量 值
MIN_VIEWSHED_RADIUS_METERS 100
MAX_VIEWSHED_RADIUS_METERS 50 000
TILE_FETCH_TIMEOUT_MS 15 000
TERRARIUM_MAX_ZOOM 15

瓦片下载失败时,那些格子高程按 0 处理而不是整体失败——一次网络抖动应该让视域”有缺口”,而不是让功能看起来坏掉。

14.4.3 它不建模什么

这是本节最该记住的部分,源码注释写得很坦白:

  • 地球曲率不建模。 50 km 半径上曲率下沉约 180 m——做无线电通视研究时这是决定性的,做”从这个观景台能看到什么”时无关紧要。
  • 大气折射不建模。
  • DEM 是全局概化模型,不是测量级 DEM。
  • 格子的南北向像元高度在整张网格上取单一常数,所以高纬度的一个高瘦方格会有轻微南北拉伸——但在这个半径上限内误差远小于一个像元。

要严格分析,用 Processing → Whitebox Toolbox → Terrain - Visibility → Viewshed,它吃你自己的 DEM 和你自己的站点矢量文件。

14.4.4 Whitebox Viewshed:严格版

参数 类型 必填 默认
input Raster / str 是 —
stations Vector / str 是 —
height float 否 2(米,默认比快捷入口的 1.8 m 高)
output str 是 —

差别一目了然:快捷入口把观察点写死成”你点的那一个点、1.8 m”;Whitebox 版接受一个站点图层(可以有很多个点)和任意视高(可以是 30 m 的塔、2 m 的人、10 m 的树冠)。

它输出的是栅格(可见为 1,不可见为 0),不是彩色叠加层——所以你可以对它做 Zonal statistics、Polygonize,或者和别的栅格做布尔运算。

14.4.5 通视与视域的区别

右键 → Quick analysis 里还有一个 Line of sight(通视)项,它和视域是两件事:

  Viewshed(视域) Line of sight(通视)
回答 从这一点,哪些区域能看见 从这一点,那一点能不能看见
输出 面/栅格覆盖层 一条视线,中间遮挡处会被标出
引擎 Web Worker 算高程栅格 直接画 MapLibre 临时样式图层
可用性 任何渲染引擎 只在主图暴露 MapLibre 实例时提供

最后一行是个真实约束:源码用 rendererCapabilities(primaryRenderer).nativeMapInstance 做门控。切到 Cesium 或 ArcGIS 主渲染器时,这个菜单项不可用。

14.5 地形夸张与三维地形

14.5.1 开关与夸张系数

Controls → Terrain 是一个开关,打开即把地图变成跟随高程的三维地形。双击这个控件可以设置垂直夸张系数(vertical exaggeration)——没有独立的对话框,双击是唯一入口。

夸张系数 效果 什么时候用
1.0 真实比例 需要判断实际坡度观感时
1.5–2.0 山势明显但不失真 演示、汇报
3.0 以上 剧烈夸张 平原地区的微地形(否则肉眼看不出来)

注意:夸张系数只影响显示,不影响任何分析工具算出来的数值。坡度、视域、汇流全都按真实高程算。别拿三维视图的”看起来有多陡”当分析结果。

14.5.2 三维地形与量测

打开 Terrain 后,Controls → Measure 面板会多出一段 Terrain (3D):

  • 距离量测会沿地表起伏累加,而不是水平直线距离。
  • 没有可用高程数据时这一段自己隐藏——注意隐藏的只是这一段,方位角那几行仍然显示,因为方位角是纯几何,不需要 DEM。
量出的量 平地/二维 开了三维地形
Distance 水平距离 坡面距离(不小于水平距离)
Heading 有 有(不受影响)
Final heading 有 有(不受影响)
Terrain (3D) 段 隐藏 显示

14.6 水文分析全链路

14.6.1 为什么第一步必须是填洼

DEM 里的”洼地”(depression / sink / pit)有两个来源:真实地形(喀斯特、冰川湖)与数据噪声(雷达测高的相位误差、插值伪影)。

D8 流向算法给每个像元指一个唯一的下游邻居。一旦遇到一个四周都比自己高的像元,它就无路可走——流向为空,汇流累积在那里断掉,河网被截成碎片,流域边界漏成筛子。

所以标准链路的第一步永远是填洼(fill)或破洼(breach):

手法 工具 做法 代价
填 fill_depressions / fill_pits 把洼地垫平到溢出口高程 真实洼地被抹掉,平地变大
破 breach_depressions_least_cost 从洼地开一条最省力的沟到下游 保留洼地,但会人工凿出沟道
混合 fill_burn 填洼同时烧入已知河网 需要先有河网数据

警告:不要跳过填洼直接算流向。结果不会报错——它会给你一个看起来像河网、但支流断成一节一节的东西。这是水文分析里最常见、也最难自查的错误。

14.6.2 完整链路

DEM
 └─▶ fill_depressions          填洼(fix_flats 顺带处理平地)
      └─▶ d8_pointer           D8 流向(每像元指一个下游)
           ├─▶ d8_flow_accum   汇流累积(上游像元计数)
           │    └─▶ extract_streams      按阈值提取河网栅格
           │         ├─▶ strahler_stream_order    Strahler 分级
           │         ├─▶ stream_link_identifier   河段(link)编号
           │         │    ├─▶ stream_link_length   河段长度
           │         │    ├─▶ stream_link_slope    河段比降
           │         │    └─▶ stream_link_class    河段类别
           │         ├─▶ subbasins               子流域(按河段切分)
           │         ├─▶ hillslopes              坡面单元(河道之间的坡地)
           │         └─▶ raster_streams_to_vector  河网转矢量
           └─▶ basins              全流域(每个出口一个)

14.6.3 每一步的确切参数

以下全部来自 whitebox-catalog-snapshot.json 原文,参数名可以直接抄进对话框。

第一步 Fill Depressions(fill_depressions)

参数 类型 必填 默认
dem Raster / str 是 —
fix_flats bool 否 true
flat_increment float / None 否 0.0001
max_depth float / None 否 —(不限)
flat_resolution 枚举 否 —(可选 garbrecht_martz / natural)
output str 是 —

fix_flats 默认开着,因为填洼会产生大片完全水平的平地,而 D8 在平地上无法定向。garbrecht_martz 是经典的平地排水算法;natural 会保留更自然的地面纹理。

第二步 D8 Pointer(d8_pointer)

参数 类型 必填 默认
dem Raster / str 是 —
esri_pntr bool 否 false
output str 是 —

esri_pntr 是编码方式的开关:关掉用 Whitebox 自己的方向编码,打开用 Esri 的 1/2/4/8/16/32/64/128。如果下游工具也要读这个指针,两边的 esri_pntr 必须一致——这是这条链路上最隐蔽的一个坑。

第三步 D8 Flow Accum(d8_flow_accum)

参数 类型 必填 默认
input Raster / str 是 —
out_type str 否 sca
log_transform bool 否 false
clip bool 否 false
input_is_pointer bool 否 false
esri_pntr bool 否 false
output str 是 —

input_is_pointer 是最容易漏的参数:填 false 时工具把 input 当成 DEM,自己先算一遍流向;填 true 时才把它当成上一步的指针。传入了 d8_pointer 的输出却忘了置 true,会得到一个莫名其妙的累积面。

out_type 默认 sca(specific catchment area,比汇水面积),另一选择是像元计数(cells)。extract_streams 的阈值是按像元数定的,所以如果 out_type 用了 sca,阈值含义就完全不同——两个参数必须一起考虑。

第四步 Extract Streams(extract_streams)

参数 类型 必填 默认
flow_accumulation Raster / str 是 —
threshold float 是 —(无默认值!)
output str 是 —
zero_background bool 否 false

threshold 没有默认值,这是有意的——它必须由你根据分辨率与研究区降雨特征来定。经验起点:

DEM 分辨率 起始阈值(像元) 大致对应的集水面积
90 m 500–1000 4–8 km²
30 m 1000–5000 0.9–4.5 km²
10 m 5000–20000 0.5–2 km²

这只是起点。判断标准是拿提取出的河网去叠真实水系图,调到能对上主要支流为止。

第五步 河网分级

工具 必填输入 作用
strahler_stream_order d8_pntr、streams Strahler 级数(源头为 1,同级汇合升一级)
horton_stream_order 同上 Horton 级数
shreve_stream_magnitude 同上 Shreve 量级(上游支流条数累加)
hack_stream_order 同上 Hack 主支流分级
topological_stream_order 同上 拓扑序编号
stream_link_identifier 同上 给每个河段(link)一个唯一 id
stream_link_length d8_pntr、linkid 河段长度
stream_link_slope d8_pntr、linkid、dem 河段比降
stream_link_class d8_pntr、streams 河段类别(按汇流关系)
length_of_upstream_channels d8_pntr、streams 上游河道总长
prune_vector_streams 矢量的河网 剪掉短于阈值的支流

所有分级工具都带 esri_pntr(默认 false)与 zero_background(默认 false),且都要求指针与河网来自同一次填洼链路。

第六步 流域划分

工具 必填输入 输出含义
basins d8_pntr 每个出口(无下游的像元)一个流域
subbasins d8_pntr、streams 按河段把流域切成子流域
watershed d8_pntr、pour_pts(矢量) 每个指定倾泻点一个流域
watershed_from_raster_pour_points d8_pntr、栅格化的倾泻点 同上,但倾泻点是栅格
hillslopes d8_pntr、streams 河道之间的坡面单元
snap_pour_points pour_pts、flow_accum(snap_dist 是参数) 把倾泻点吸附到高汇流处
unnest_basins d8_pointer、pour_points(矢量) 拆开嵌套流域
isobasins dem(另有 target_size 参数) 等面积流域

watershed 之前几乎总要跑 snap_pour_points:你手上那个水文站坐标是经纬度,落在汇流累积的低值处(也就是坡上),不吸附过去会得到一个荒谬的小流域。snap_dist 是搜索半径,单位是像元数,不是米。

14.6.4 在界面上跑完这条链

路线 A:Whitebox Toolbox(WASM,推荐)

  1. Processing → Whitebox Toolbox。
  2. 搜索框输入 fill_depressions,选中,表单里 dem 选你的 DEM 图层,Run。
  3. 拿到输出图层后,搜索 d8_pointer,dem 选上一步的输出,Run。
  4. 搜索 d8_flow_accum,input 选指针图层,把 input_is_pointer 勾上,按需设 out_type,Run。
  5. 搜索 extract_streams,flow_accumulation 选累积图层,填 threshold,Run。
  6. 搜索 strahler_stream_order,d8_pntr 选第三步的指针、streams 选第五步的河网,Run。
  7. 需要矢量河网时再跑 raster_streams_to_vector。

每一步的输出都是 Cloud Optimized GeoTIFF,直接加成图层。

路线 B:Model Builder 串成一张图

把上面六步拖成一条链,就是第 13 章讲过的那种栅格模型。好处是换 DEM 只改一个 Input。注意 extract_streams 的 threshold 是必填且无默认,模型里必须显式填一个数。

路线 C:sidecar

Whitebox Toolbox 里取消勾选 Run locally (WASM),改走 Python sidecar。差别是 sidecar 能读写原生文件路径,所以可以处理磁盘上一整个目录的 DEM,也能写出比浏览器内存允许的更大的中间结果。

14.6.5 流向算法的选择

D8 只是众多流向算法之一,Whitebox 提供了完整的一族:

算法 工具前缀 特点 适合
D8 d8_ 前缀 单流向,最简单最快 通用河网提取、教学
D-infinity dinf_ 前缀 多流向,按角度分配 坡面扩散过程、土壤侵蚀
FD8 fd8_ 前缀 D8 加按坡度分配 折中方案
Rho8 rho8_ 前缀 随机 D8,多次运行取平均 需要消除 D8 的方向偏置
Quinn quinn_flow_accumulation 多流向 与 TOPMODEL 类模型配合
MD-infinity mdinf_flow_accum 改进的多流向 —
Qin qin_flow_accumulation Qin 等人的方法 —

选型建议:只有一条链路要交付、且只需要河网和流域,就用 D8(后续所有分级工具都是 D8 的)。需要模拟坡面漫流、土壤流失、污染扩散这类”不该只有一条路”的过程,用 D-infinity。不确定就先 D8,它是这个目录里工具链最完整的一族。

另外还有两个现成的封装工具可以直接用:

  • flow_accum_full_workflow(Flow Accum Full Workflow)——一次完成填洼、流向、累积。
  • minimal_dispersion_flow_algorithm(Minimal Dispersion Flow Algorithm)——以最小离散度为目标的流向。

提示:第一次做水文,用 flow_accum_full_workflow 比手工串四步快得多,而且它内部保证了 esri_pntr 一致。等确认结果合理,再拆成显式步骤以便调参。

14.7 USGS NLDI 工作流

前面的水文分析都是”从 DEM 算出来”的。NLDI 走的是另一条路:从已有的国家水文数据集中查出来。两者互补——DEM 给的是地表形态,NLDI 给的是官方水系编号与站点。

14.7.1 入口与三种追踪模式

Plugins → Web Services → USGS NLDI。

  1. 选追踪模式:Complete flowline(完整河段)、Upstream only(仅上游)、Downstream only(仅下游)。
  2. 在地图上点一个点。

插件调用 NLDI 的 nldi-flowtrace 过程,把返回的 NHD 河段画成蓝色;当点击点不在河段上时,还会用基于高程的 raindropPath 画成橙色;点击点本身标红色。

14.7.2 从点到流域

追踪之后,GeoLibre 会对该坐标调用 NLDI 的 hydrolocation,拿到 COMID(河段的唯一编号)。有了 COMID:

  1. Basin from hydrolocation 请求简化的上游流域。
  2. 在导航区填一个公里数(搜索半径),点 1. Load sources & plot navigation 加载可用的目录列表。
  3. 从列表里选要画的图层,点 Plot another navigation layer。

可选的导航目录包括:

目录 id 内容
NHD flowlines 上下游河段
ca_gages / nwissite 河流水文站
nwisgw 地下水井
gfv11_pois Geospatial Fabric 点位
huc12pp HUC12 倾泻点
nmwdi-st 新墨西哥州水源点
其它 NLDI 返回的目录 —

导航结果(河段与点)统一画成紫色。

这是一条链式工作流:地图点 → hydrolocation → COMID → 流域或导航请求 → 渲染。每一步的输入都是上一步的输出,中间没有一个需要你手工填的 id。

14.7.3 结果导出与落地

按钮 结果
Export rendered results to GeoJSON 把选中点、河段、雨滴路径、流域、以及所有已绘制的导航要素存成一个 FeatureCollection
Add rendered results to GeoLibre Layers 把当前结果复制进图层栈,归入一个叫 USGS NLDI results 的组

导出文件里每类结果靠 _nldiLayer 属性区分。这一点很实用:一次导出里混着五类几何,用 SQL 或 Select by value 按 _nldiLayer 拆开就行。

Add rendered results 与导出有一个关键区别:复制进图层栈的图层留在项目里,即使之后清掉了临时的 NLDI 地图叠加也还在。

14.7.4 服务依赖

插件用的是公开服务 https://api.water.usgs.gov/nldi。两个前提:

  1. 需要网络。
  2. 该服务的 CORS 策略必须放行——这是浏览器直连的固有限制,不是 GeoLibre 的问题。企业网络代理掉 USGS 时,这个插件会整体不可用。

14.8 参数选择:Z 因子、单位、投影

14.8.1 Z 因子到底在改什么

Z 因子是高程方向的线性乘数。它改变的是”高程差相对于水平距离的比值”,也就是坡度。

场景 Z 因子 理由
DEM 与水平坐标同为米 1 单位一致,不需要补偿
DEM 高程是米,水平坐标是度 需要换算 度与米不同量纲,见 14.8.3
DEM 高程是英尺,水平是米 0.3048 单位换算
想强调微地形(制图) 2–5 只用于可视化,绝不能用于分析

警告:拿调高的 Z 因子去算坡度再去做工程判断,是本章最容易造成实际损失的错误。Z 因子只在制图风格里用得着;分析时它必须反映真实单位。

14.8.2 单位:坡度百分比与角度

已见 14.2.3。补充一个易错点:Whitebox 的 slope 与 GeoLibre Raster 工具的 Slope 默认值都是 degrees,但有些工程规范要求 percent。导出前确认一下字段值的范围——如果最大值是 90 左右,那是角度;如果是几百,那是百分比。

14.8.3 投影:这是最重要的一节

所有地形与水文计算都假设像元是正方形且边长为常数。 这个假设只在投影坐标系里成立。经纬度(EPSG:4326)里:

  • 1 度纬度约 111.32 km(各处基本一致)。
  • 1 度经度约 111.32 乘 cos(纬度) km(随纬度剧烈变化)。

在纬度 40 度处,一个 1 度见方的像元实际上是 111 km 乘 85 km 的矩形。坡度按”高差除以像元数”算出来会偏大近 30%,而且偏的方向随坡向变化——东西向坡和南北向坡的误差不一样。

正确做法:

  1. 先把 DEM 重投影到适合研究区的投影坐标系,例如 UTM、Albers 等积投影、或中国常用的 CGCS2000 高斯-克吕格分带。
  2. 重投影后确认像元是正方形(X/Y 分辨率相等)。
  3. 再跑坡度、坡向、曲率、水文分析。

Processing → GeoLibre Toolbox → Raster → Reproject(需要 sidecar),或 Whitebox 的投影类工具。

研究区尺度 推荐投影 说明
单个小流域(小于 100 km²) 所在 UTM 带 变形可忽略
省级 Albers 等积(按中央经线定制) 面积守恒
全国 Albers / Lambert 不要用 Web Mercator
城市/场地 地方平面坐标 精度最高

注意:Web Mercator(EPSG:3857)不能用于面积与距离计算。它是为切片显示设计的,高纬度面积夸张可达数倍。地图画布用它没问题,但喂给 slope 就是错的。

14.8.4 一个速查表

工具 对投影敏感 对 Z 因子敏感 备注
hillshade 是 是 经纬度下光照方向会失真
slope 是 是 双边敏感,最容易错
aspect 是 否 方向不受 Z 影响
曲率族 是 是 量纲是 1/长度
fill_depressions 轻微 否 只看相对高差
d8_pointer 轻微 否 只看相对高差
d8_flow_accum 是 否 面积换算依赖像元真实大小
extract_streams 是 否 阈值是像元数,像元大小变了阈值就变了
watershed / basins 轻微 否 拓扑性质为主
viewshed 是 否 距离按像元真实米数换算

14.9 结果验证与常见错误

14.9.1 六个必做的验证动作

验证 怎么做 期望
高程范围 图层的 Metadata,或 Zonal statistics 看 min/max 与研究区实际海拔相符;出现 −32768 说明 NoData 没处理好
坡度分布 对坡度图层做直方图(Dashboard 或图表) 大部分落在 0–30 度;出现 89 度尖峰说明有断崖或 NoData 边界
河网密度 河网总长除以流域面积 30 m DEM 常见 0.5–2 km/km²;偏离一个量级说明阈值错了
河网叠合 把提取的河网叠到影像或真实水系上 主河道吻合;不吻合多半是没填洼或 esri_pntr 不一致
流域闭合 检查流域栅格有没有孤立小斑块 成百上千个小斑块说明有洼地残留
汇流单调 沿一条河道从上游到下游抽查累积值 必须单调递增;出现下降说明指针被改过

14.9.2 十个常见错误

现象 最可能的原因 修法
坡度量出来全是很小的值 DEM 是经纬度,像元被当成 1×1 米 重投影到投影坐标系
坡度出现 89 度以上 NoData 边界、或 DEM 有拼接缝 先 fill_missing_data,或裁掉边界
山体阴影看起来是凹的 azimuth 被设到东南(135 左右) 改回 315
等高线互相缠绕 等高距小于垂直精度的 2 倍 加大 interval
流向图大片空白 没填洼 先 fill_depressions
汇流累积到处都是 1 input_is_pointer 忘了置 true,或 out_type 用了 sca 按 14.6.3 第三步修正
河网密得像网兜 threshold 太小 加大阈值
河网稀疏、主河道都断 threshold 太大 减小阈值
分级工具报错或结果全 1 指针与河网不是同一次链路产出 重跑整条链
视域是个规整的正方形 瓦片全部下载失败,高程全为 0 检查网络与 CORS;换 Whitebox viewshed

14.9.3 NoData 的处理

DEM 的 NoData 常常是 -9999、-32768 或 NaN。这些值一旦参与运算:

  • 坡度会在边界炸出极端值。
  • 填洼会把 NoData 当成深坑,整片被填。
  • 视域会把 NoData 当成海平面。

处理顺序:加载后第一件事是确认 NoData 值(图层 Metadata),必要时用 fill_missing_data(Fill Missing Data,Terrain 分类),或先裁掉边界。Whitebox 工具一般会尊重输入的 NoData 元数据,但从别处转来的 GeoTIFF 常常丢了这一项。

14.10 两个完整案例

14.10.1 案例一:小流域河网与子流域提取

目标:给一个约 200 km² 的山区流域,产出河网矢量、Strahler 分级、子流域面。

准备:

  1. 取 30 m DEM(Copernicus DEM 或 SRTM),确认覆盖完整流域并外扩一圈(流域边界要靠分水岭,边缘截断会导致出口不唯一)。
  2. Reproject 到该区域的 UTM 带(见 14.8.3)。
  3. 确认像元是正方形:X/Y 分辨率相等,且 CRS 单位是米。

链路(Whitebox Toolbox,WASM):

步 工具 关键参数 检查
1 fill_depressions fix_flats 为 true,flat_resolution 用 garbrecht_martz 输出与输入的高程差应集中在洼地
2 d8_pointer esri_pntr 为 false(默认) 记下这个值,后续必须一致
3 d8_flow_accum input_is_pointer 为 true,out_type 二选一并记住 出口处累积值应等于总像元数(cells 模式)
4 extract_streams threshold 从 2000 起试 与真实水系叠合调参
5 strahler_stream_order 指针加河网,esri_pntr 同上 级数应集中在 1 到 5
6 stream_link_identifier 同上 每个河段一个连续 id
7 subbasins 指针加河网 子流域数量约等于河段数量
8 raster_streams_to_vector 河网栅格 得到矢量线,属性里带栅格值
9 hillslopes(可选) 指针加河网 河道之间的坡面单元

结果验证(照 14.9.1 全走一遍):

  • 汇流累积出口值等于流域总像元数(cells 模式)。不等说明 DEM 边缘被截断,或填洼把水引到了别处。
  • 河网密度落在 0.5–2 km/km²。
  • 子流域面积之和等于流域总面积(误差小于 1%)。
  • 把河网叠到影像上,主河道必须落在真实谷底。

导出:

  1. 河网矢量层,图层菜单 Export,选 GeoPackage 或 FlatGeobuf。
  2. 子流域面层,同上。
  3. 需要给同事看的三维化产品,用 Export → GeoTIFF (COG)。

提示:这套链路在 Model Builder 里可以一次搭好(见第 13 章的栅格链结构),换流域只改 Input 的 Source layer。但 threshold 必须跟着分辨率改——模型里写死的是个数,不是物理面积。

14.10.2 案例二:观景点视域与通视分析

目标:评估一个观景台能看到多大范围,以及它能不能看到对面山头的塔。

A. 快速判断(2 分钟)

  1. 把地图移到观景台位置,缩放到 1–2 km 尺度。
  2. 右键 → Quick analysis → Viewshed from here,先选 2 km。
  3. 等几秒(瓦片要下载)。结果图层 Viewshed (2 km) 出现。
  4. 把该图层透明度调到 50% 左右,叠在影像上看覆盖。
  5. 换 5 km、15 km 各跑一次,对比覆盖范围的增长是否合理。

B. 通视判断

  1. 右键观景台,Quick analysis → Line of sight(前提:主渲染器是 MapLibre)。
  2. 点击塔的位置。
  3. 视线会被画出;沿途遮挡处会被标注。

C. 严格分析(用自有 DEM)

  1. Add Data → Raster Layer 加载 1 m 或 5 m 的 LiDAR 派生 DEM。
  2. 新建一个点图层(或用 Add Data → Delimited Text Layer 从 CSV 导入),放观景台坐标。
  3. Processing → Whitebox Toolbox,搜索 viewshed。
  4. input 选 DEM 图层;stations 选观景台点图层;height 填 1.8(人眼高度;若观景台有栏杆平台,用 1.8 加平台高度)。
  5. Run。得到可见性栅格(1/0)。
  6. 把这层叠到 DEM 晕渲上,用 Reclassify 把 0/1 映射成”不可见/可见”两色。
  7. 想看面积:Zonal statistics(sidecar),或在 SQL 里按像元面积求和。

两种方法的结论应该大致一致。如果不一致,优先信 C——它用的是你自己的 DEM 和明确的视高,而 A 用的是全局概化瓦片和写死的 1.8 m。

D. 三条容易忽略的注意点

注意点 说明
树木与建筑 DEM 是裸地高程。10 m 高的树会让实际视域比计算出的小很多。严谨做法是把树高加成 stations 的 height,或用 DSM 代替 DEM
视高含义 height 是高于地面的高度,不是海拔。填 1.8 表示站在地上的人眼
地球曲率 5 km 内可忽略;50 km 上限处曲率下沉约 180 m,必须用专业工具另行补偿

14.11 本章小结

  • DEM 在 GeoLibre 里就是单波段栅格,六条入口:本地文件、COG URL、Download Global DEM、STAC Layer、Planetary Computer、Earth Engine。三维地形底图(Terrarium 瓦片)不是图层,但视域快捷入口会用同一套瓦片。
  • Download Global DEM:参数只有 bbox(west,south,east,north)与 bbox_crs(只接受 4326);瓦片 512 px、最高 12 级、单次最多 64 块、纬度硬边界正负 85.051129。范围越大分辨率越粗。
  • 地形产品两条路径:GeoLibre Toolbox → Raster(Hillshade / Slope / Aspect 有客户端引擎)与 Whitebox Toolbox(WASM)。Hillshade 的 altitude 默认值不同:GeoLibre 是 45,Whitebox 是 30。
  • Aspect 没有 z_factor——方向不受高程缩放影响。曲率族有近二十个工具,常用的只有 plan / profile / mean / gaussian 四个。
  • Contour 在 GeoLibre 侧需要 sidecar;Whitebox 的 contours_from_raster 是 WASM,多了平滑与简化两个旋钮。等高距应不小于 DEM 垂直精度的 2 倍。
  • 视域有两条路:右键 Quick analysis(Terrarium 瓦片、观察者 1.8 m、2/5/15 km 预设、输出图像叠加层)与 Whitebox viewshed(自有 DEM 加站点图层加自定义视高、输出 1/0 栅格)。前者不建模曲率与折射,后者是严格版。
  • 快捷视域的半径硬边界是 100 m 到 50 km,算法是 O(n³) 的 R3 式视线,跑在 Web Worker 里。Line of sight 只在主渲染器暴露 MapLibre 实例时可用。
  • 三维地形的垂直夸张通过双击 Controls → Terrain 控件设置;它只影响显示,不影响任何分析数值。
  • 水文链路:fill_depressions → d8_pointer → d8_flow_accum → extract_streams → 分级工具 → subbasins / hillslopes;basins 走另一支,watershed 需要倾泻点且通常先 snap_pour_points。
  • 三个最容易错的水文参数:input_is_pointer(填汇流累积时必须置 true)、esri_pntr(链路上必须一致)、threshold(无默认值,随分辨率变)。
  • USGS NLDI 走的是”查国家水系”而不是”从 DEM 算”:地图点 → flowtrace → hydrolocation → COMID → 流域或导航;导出靠 _nldiLayer 属性区分结果类型。依赖 https://api.water.usgs.gov/nldi 与 CORS。
  • 投影是地形分析的一号变量。经纬度 DEM 直接算坡度会偏大近 30% 且方向相关;Web Mercator 绝不能用于面积与距离计算。
  • NoData 必须在上游处理掉,否则坡度、填洼、视域三处会同时出错。

14.12 实践练习

  1. 对比两个 Hillshade 默认值:对同一份 DEM 分别用 GeoLibre Toolbox → Raster → Hillshade(altitude 45)与 Whitebox Toolbox 的 hillshade(altitude 30)生成晕渲图,把两层的混合模式都设成 Multiply 叠在同一份高程分色图上。描述两者在微地形表现上的差别,并说明各自适合什么场景。

  2. 验证投影影响:取一份经纬度 DEM,先在原地跑 slope(degrees),记录最大值与均值;再用 Raster → Reproject 转到合适的 UTM 带后重跑。对比两次的统计量,并解释为什么差异不是随机的。

  3. 跑通水文链路:按 14.10.1 的九个步骤走完,重点验证三件事——d8_flow_accum 出口累积值是否等于总像元数、threshold 从 2000 调到 500 时河网条数如何变化、subbasins 面积之和与流域总面积是否一致(误差应小于 1%)。

  4. 两种视域方法对照:在同一座山头上,先用右键 Quick analysis → Viewshed from here(5 km) 算一次,再用自有 DEM 走 Whitebox viewshed(视高 1.8 m)算一次。把两个结果叠在一起,量出它们的重合比例,并列出至少三个导致差异的原因。

  5. NLDI 与 DEM 交叉验证:在一个你熟悉的流域上,用 Plugins → Web Services → USGS NLDI 追踪主河道并取上游流域;同时用 DEM 跑 basins。把两个流域面叠加,观察边界差异,并解释哪些差异来自数据源不同、哪些来自 DEM 分辨率。

  6. 参数陷阱排查:故意犯三个错——d8_flow_accum 不勾 input_is_pointer、分级工具把 esri_pntr 改成 true、extract_streams 的 threshold 填 10。分别记录输出长什么样,并写出你怎么从结果外观反推出是哪一步出的错。

14.13 延伸阅读