oss

GRASS 把网格写进程序

10 条来源 8 条一手来源 已翻译 2026年9月13号

正文
GRASS 项目 2023 年布拉格社区会议期间,两位贡献者隔着工作桌相视微笑,桌上放着打开的笔记本电脑。

2023 年 6 月,GRASS 社区会议在布拉格举行,贡献者们正在协作。这场聚会也庆祝了项目诞生 40 周年。照片记录了地图集所支持的协作方式:各自独立的工作命名空间可以共用项目数据。照片由 GRASS 项目发布。[10]

GRASS 可以执行一条完全有效的栅格表达式,回答的空间问题却与操作者的原意不同。即使表达式指定了正确的高程地图并使用正确的阈值,输出仍会覆盖错误范围,落到错误的像元网格上,或悄然排除研究区。这种偏差有其明确的架构根源:在 GRASS 中,命令字符串只是程序的一部分。

其余部分存在于分析环境中。项目(project)确定坐标参考系;地图集(mapset)给出写入命名空间和搜索路径;计算区域(computational region)给出工作范围、分辨率与对齐方式;可选的栅格掩膜则改变参与计算的输入像元。大多数栅格模块都会读取这些状态,各条命令行也就省去了逐项重复填写。[2][4][5][7]

把这些状态称作一组默认值,仍然低估了它们。它们更接近一份空间执行契约。有了这份契约,长串地理处理模块便可组合运行:工作网格一经明确,后续输出自然对齐,各模块也省去了重新协商网格的环节。与此同时,可复现性也提出了记录义务。只保存最终的 r.mapcalc 表达式,就像保存数据库查询时遗漏了用于名称解析的数据库或模式。

在 GRASS 格外漫长的生命史中,这套模型以不同形态延续至今。项目于 1982 年在美国陆军建筑工程研究实验室启动,管理工作后来转交给国际自由软件社区。[1] 当代界面与早期系统已经迥然不同,那个持久的观念依然清晰可见:分析以预先准备好的地理工作空间为单位,彼此无关的零散文件集合处在这一单位之外。

项目确定地理基准;地图集确定工作结果的去处

一个 GRASS 项目容纳同一坐标参考系下的数据。项目内部由地图集划分子项目或工作环境。每个项目都有一个名为 PERMANENT 的地图集,按照惯例存放共享基础数据和项目默认区域。分析人员可以为不同成员、方案或批处理任务建立各自的地图集,同时读取同一组基准地图。[2][3]

当前地图集是随会话而定的写入边界。模块可以在其中创建、修改或删除地图;其他经授权的地图集则通过搜索路径供读取。这套规则管理命名空间,不承担安全沙箱的功能,也不保证 PERMANENT 永远不可更改:用户只要把另一个有权访问的地图集设为当前地图集,就可以向其中写入。它带来的架构收益范围明确而实用——日常工作的结果有一个清楚的去处。[2][3]

读取过程更为细微。名为 elevation 的输入会解析为搜索路径上第一个匹配的地图,当前地图集排在首位,PERMANENT 默认也在路径中。elevation@PERMANENT 则明确指定某个地图集。数学表达式保持原样时,这一区别依然足以改变结果。如果临时地图集含有自己的 elevation,未限定名称就会遮蔽共享基准;如果两个地图集保存的搜索路径顺序不同,同一个未限定名称也会绑定到不同数据。[2]

地图集因此很适合并行运行多个方案,溯源信息仍需主动记录。让每次运行使用独立的当前地图集,输出便不会相互覆盖。重要输入还应以 @mapset 限定,否则共享名称带来的便利会成为未声明的依赖。

计算区域是一项隐式参数

每个地图集都有自己的当前计算区域,通常持久保存于名为 WIND 的文件中。PERMANENT 地图集还保存 DEFAULT_WIND,用于提供项目默认区域,也是新地图集的初始区域。修改当前区域时,已存储地图保持原状;改变的是后续区域感知型任务依据哪个网格求值。[3][4]

这套网格记录北、南、东、西四向边界,以及行、列分辨率;三维栅格还增加垂直方向。大多数栅格模块和显示模块遵循它,矢量模块通常不受它约束,导入、投影和显式重采样工具也属于例外。g.region 可以复制某幅栅格的网格,指定一个具名范围,设置行数或分辨率,对齐边界,保存具名区域,或输出当前生效的设置。[4][5]

分辨率会直接影响分析含义与计算规模,远超外观层面的差异。设想一个使用米制投影坐标系的会话,研究区约为 10 千米见方。像元边长为 10 米时,区域内约有 100 万个像元;缩小到 1 米时,像元数约为 1 亿,在计入算法自身开销之前,数值数量已经扩大 100 倍。因此,res 中一个字符的变化,足以同时改变分析含义和计算成本。边界对齐会调整确切行数,但数量级的变化仍然存在。

在已知的 GRASS 会话中,一段简短且便于检查的设置可以这样开始:

g.region -a raster=elevation@PERMANENT res=10
g.region -p format=json > run-region.json
r.mapcalc "high_ground = if(elevation@PERMANENT >= 500, 1, null())"

第一行明确采用目标源数据的范围和 10 米网格,并让边界与分辨率对齐。第二行把实际生效的区域序列化为机器可读格式。第三行在当前地图集中写入新栅格,并把公式记入结果的标题和历史记录。[4][6] 这份 JSON 文件是重要证据,完整的运行清单还应包括活动掩膜、地图身份、软件构建版本和进程级覆盖设置。

栅格输入在区域网格上相遇

默认情况下,GRASS 通过当前区域读取栅格输入。输入范围不同时,系统会裁剪超出部分,或用 NULL 像元补齐缺口;输入分辨率或对齐方式不同时,系统会在运行中以最近邻选择将其重采样到工作网格。栅格输出采用当前区域的边界和分辨率,原始输入保持不变。[5]

这项行为让栅格代数可以直接组合多幅地图,省下了为每个源数据预制匹配副本的步骤,其中也包含明确的方法取向。最近邻选择适合保留类别,对连续表面而言,科学上合适的方法仍需另行判断。分析需要插值或聚合时,工作流应使用显式重采样模块并记录所选方法,避免把自动网格匹配视为中性的处理。[5]

r.mapcalc 尤其清楚地展示了这种组合方式。它逐像元计算表达式,按照地图集搜索路径解析未限定名称,根据运算符传播 NULL,并在当前地图集中创建具名输出。其默认计算区域就是当前区域;当前版本还提供 unionintersect 模式,可以依据输入地图生成临时区域,同时保留地图集中存储的区域设置。[6]

因此,公式记录是溯源所需的信息,单凭公式仍然不足。两份结果的历史记录可以出现同一表达式,栅格头信息却显示不同网格。表达式和网格即使完全相同,读取的输入仍会因名称遮蔽而不同。审查时需要追问完整的计算单元:“哪条方程在哪一套格网上运行,又解析到了哪些地图?”

掩膜是另一项更安静的空间输入

计算区域描述一个矩形,栅格掩膜则规定矩形内哪些像元计入分析。掩膜处于活动状态时,系统读取已有栅格输入,会把掩膜之外的数据当作 NULL。输入为栅格时,r.mask 通常创建一个名为 MASK 的重分类栅格;在删除或重命名之前,该掩膜会持续生效,环境变量 GRASS_MASK 还可以选用其他掩膜名称。[7]

零值带来一处格外尖锐的差异。采用默认类别选择时,r.mask raster=input 会把所有非 NULL 输入值,包括零,转换为 1。如果零原本表示“范围之外”,这条调用仍会把它纳入。相较之下,直接创建为实际 MASK 的栅格遵循布尔掩膜语义:零和 NULL 被排除,非零整数值被纳入。两种创建路径乍看相近,含义却不相同。[7]

掩膜作用在读取阶段,无法充当覆盖所有写操作的通用模板。读取 elevation 的表达式会把掩膜外像元视为 NULL。常量表达式可以完全不读取栅格,因此,仅凭活动掩膜便认定每份输出都经过裁剪,会带来错误结果。[6][7]

在交互式会话中,如果几十项操作都要采用同一个流域或行政边界,这种环境状态十分好用。掩膜只需设置一次,分析人员便可省去反复填写裁剪操作数的步骤。在无人值守的流水线中,同样的持久性会积累为技术债。一个被遗忘的掩膜可以生成外观整洁、内部一致却缺失部分数据的输出。经得起复核的运行记录应明确写下掩膜是否存在;如有掩膜,则准确写明由哪幅栅格提供,以及该栅格的生成方式。

可复现性的基本单元大于一条命令

这套架构对应着一套具体的操作方式。每次运行从具名项目和一个全新且名称唯一的地图集开始。为基准输入写出完整限定名。分析前设置计算区域并将其导出。主动建立或移除掩膜,避免继承先前交互式会话遗留的状态。把 GRASS 发行版本、模块参数、输入校验和或稳定数据集标识符、搜索路径、区域 JSON 与掩膜状态连同输出一起保存。导出前再检查输出的头信息和历史记录。

相比打开 GeoTIFF 后调用函数,这套做法的确更重。2025 年,一项独立的数字城市规划工具评述从用户体验出发,也指出了相近的采用门槛:评述特别提到 GRASS 分析功能深厚,同时也认为专业学习曲线陡峭。[9] 只需要少量无状态转换的团队,选择更小型的库很合理。构建可重复水文、地形、影像或土地变化流水线的团队,则可以从这种工作空间中获益:单一坐标参考系、经审慎选择的工作网格和彼此分离的运行命名空间,都成为可以长期沿用的概念。

GRASS 8.5.0 显示出项目正尝试让这套成熟引擎接入更现代的自动化方式。该版本于 2026 年 5 月发布,新增高层 grass.tools Python API、更多 JSON 输出和面向数组的直接栅格交换方式,还改进了并行执行,其中包括针对 r.mapcalc 的工作。[8] 这些接口简化了操作流程,空间执行契约依然存在。一次 Python 调用仍需明确由哪个项目、地图集、区域和掩膜赋予其含义。

据此理解 GRASS 的架构,其实用价值便清晰起来。计算区域比屏幕上当前可见的矩形包含更多含义,地图集的作用也超出普通文件夹。它们连同名称解析与掩膜,都是计算的输入;这些输入被持久保存,让一整套工具可以遵循共同约定。当工作流以记录公式时的同等谨慎记录它们,GRASS 的环境状态便从陷阱转化为其原本承担的角色:对地理问题的共同定义。

来源

  1. GRASS 项目,“GRASS 的历史”——CERL 起源、转由社区管理的过程与开源历程。
  2. GRASS 8.5 文档,“GRASS 项目”——单一坐标参考系项目、地图集、当前地图集写入、搜索路径、限定名称与临时工作空间。
  3. GRASS 8.5 文档,“GRASS 数据库”——项目与地图集的存储层级,以及 WINDDEFAULT_WIND 区域文件。
  4. GRASS 8.5 手册,“g.region”——计算区域的范围、分辨率、对齐、持久保存、作用范围与 JSON 输出。
  5. GRASS 8.5 手册,“GRASS 中的栅格数据处理”——由区域控制的输出、输入裁剪、填充、缩放、NULL 行为与例外。
  6. GRASS 8.5 手册,“r.mapcalc”——栅格表达式、地图集名称解析、计算区域模式、输出位置与公式历史记录。
  7. GRASS 8.5 手册,“r.mask”——持久栅格掩膜、创建路径、零与 NULL 的语义、移除操作与具名掩膜。
  8. GRASS 项目,“GRASS 8.5.0 发布”——2026 年 5 月的发布背景、grass.tools、JSON 接口、数组交换与并行处理变化。
  9. Samuel Hanan 与 Neil Carhart,《赋予公众参与能力的城市规划工具评述》,Frontiers of Urban and Rural Planning——对 GRASS 分析深度和采用学习曲线的独立比较。
  10. GRASS 项目,“2023 年布拉格 GRASS 社区会议”——活动记录与封面照片的来源图集。
Previous FlightGear 让驾驶舱成为动态命名空间

Recommended In oss

Matched by subject and format