开头就不绕弯子了,直接说重点。Arctoolbox里的3D Analyst模块,平时大家用得最多的就是生成等高线、做剖面分析,真正被忽略的其实是它内置的栅格计算能力。栅格计算(Raster Calculator)相当于把每个像元当成一个变量,用数学表达式批量处理整个图层,在DEM分析、坡度坡向计算、区域筛选这些场景里特别好用。这一期教程是系列第二篇,专门聚焦栅格计算的完整实操,从界面逻辑到表达式规则,再到三个可以直接抄作业的案例,把常见报错也一并梳理清楚。适合正在学ArcGIS的在校学生,以及工作中经常和DEM、土地利用数据打交道的规划、测绘、地质行业的朋友。
1. 栅格计算到底在算什么:核心思路与设计拆解
1.1 栅格计算器的本质:逐像元的“批处理”
很多人第一次打开Raster Calculator的时候会有点懵,界面就这么大点地方,一个输入框、几个按钮,怎么看都不像能干大事的样子。但栅格计算的核心逻辑其实特别朴素,就是对图层中每一个像元(Cell)执行同一个数学表达式,输出结果是一个全新的栅格图层。
举个例子,你手里有一份高程DEM数据,值域从200米到2800米。现在你想把低于500米的区域全部标记出来,手工一个个像元去改显然不现实。但用栅格计算器,一行表达式就完成:
text复制"dem" < 500
这行表达式的执行结果,是原DEM里每一个像元都做一次“< 500”的判断,条件为真的像元返回1,条件为假的返回0,最终输出一张只包含0和1两个值的二值栅格图。你可以把它理解成Excel里的IF函数批量填充,只不过Excel处理的是表格单元格,栅格计算器处理的是地理空间上的像元网格。
这种逐像元运算的思路,让栅格计算在数据分析里有了极高的自由度。它不局限于“小于”这种简单判断,还能做多图层叠加、代数运算、三角函数变换、条件嵌套组合,甚至把多个栅格图层当成变量来构建复杂的地理模型表达式。这也是为什么很多做生态敏感性评价、洪水淹没分析、适宜性选址的技术人员,离不开栅格计算器的原因。
1.2 为什么非要用栅格计算而不是手工操作
先说一个实际场景。早些时候有朋友做区域坡度分级,数据是一份30米分辨率的DEM,他一开始用的是Spatial Analyst里的Reclassify工具,按坡度阈值手动分了好几级。数据量小的时候没问题,但换到全市域范围、两层融合的DEM之后,反复调整阈值就变得非常痛苦,每改一次阈值都要重新加载、重新设置、重新跑一遍工具。
栅格计算器在这种场景下碾压式的优势就体现出来了。你把表达式写成一个公式,改一个数字,重新执行一次就算完了,不存在反复操作UI的问题。比如坡度分级可以用一条表达式把多级判断全部塞进去:
text复制Con("slope" < 5, 1, Con("slope" < 15, 2, Con("slope" < 25, 3, 4)))
这条语句的意思很直白:坡度小于5度的区域赋值为1,5到15度之间赋值为2,15到25度赋值为3,大于等于25度赋值为4。一次执行完成全部分级,不需要打开好几个工具来回倒腾数据。逻辑清晰、可复制、可调整,这在做批量处理的场景里效率提升是指数级的。
1.3 3D Analyst在栅格计算里的定位
这一系列教程的标题是“3D Analyst之栅格计算”,有必要先说明一下3D Analyst和Spatial Analyst的关系。在ArcGIS的体系里,栅格计算器严格来说属于Spatial Analyst扩展模块的能力,但3D Analyst依赖并集成了Spatial Analyst,所以只要勾选了3D Analyst扩展模块,栅格计算器通常也能正常使用。更关键的是,3D Analyst自身附带了一批三维地形分析函数,比如Slope(坡度)、Aspect(坡向)、Hillshade(山体阴影)、Contour(等高线)等,这些函数可以直接在栅格计算器的表达式里调用,从DEM里实时算出一个派生地形图层,再跟其他栅格数据做复合运算。
比如在山地光伏选址的场景里,需要同时考虑坡度、坡向、高程三个因素,用栅格计算器就能把地形因子全部串联起来,一步算出一个综合打分图层。这也是3D Analyst在这一期内容里的核心价值:不光是能算,还能把地形分析嵌入到计算表达式里,形成一条完整的分析链路。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 动手前的准备:数据、参数和表达式规则
2.1 打开栅格计算器与界面布局
栅格计算器的打开路径有两个,根据自己的习惯任选一个:
- 右键ArcToolbox,展开3D Analyst Tools → Raster Math → Raster Calculator
- 在ArcMap的工具栏空白处右键,调出Spatial Analyst工具栏,点Raster Calculator图标
打开后界面就是经典的“表达式输入框+图层列表+工具按钮”三块结构。左侧上方是当前地图文档里加载的所有栅格图层和矢量图层列表,双击就能把图层名插入到表达式里,避免手打路径出错。左侧下方是运算符列表,加减乘除、比较、逻辑运算符都在这里。中间是数学函数列表,包括三角函数、对数、幂运算、取整等。右侧最下面有一个“Output raster”输出路径设置栏,这里需要手动指定结果保存的位置。
这里有一个关键点要提醒:表达式输入框里插入的图层,ARC会自动加上英文双引号,比如 "dem"。这个双引号是不能省的,它代表这是一个图层的名称引用,而不是一个字符串常量。如果你输入的表达式里图层名没有带双引号,执行时会直接报“Invalid expression”。
2.2 运算符与函数清单,以及优先级优先级陷阱
栅格计算器的运算符看起来跟数学课上学的大差不差,但有几个约定俗成的规则和常见的坑需要注意。先把完整的运算符基本盘列出来。
| 类型 | 符号 | 说明 | 示例 |
|---|---|---|---|
| 算术运算符 | +、-、*、/ | 逐像元四则运算 | "pop" * 0.5 |
| 幂运算 | ^ 或 Power() | 实现指数计算 | "dem" ^ 2 |
| 比较运算符 | <、>、<=、>=、==、!= | 返回1或0 | "dem" > 500 |
| 逻辑与 | & | 两边都真才为真 | ("a" > 1) & ("b" > 2) |
| 逻辑或 | | | 有一边真就为真 | ("a" > 1) | ("b" > 2) |
| 逻辑非 | ~ | 取反 | ~("dem" > 500) |
| 条件函数 | Con(条件, 真值, 假值) | IF判断 | Con("dem" > 500, 1, 0) |
| 空值处理 | SetNull(条件, 真值) | 把特定值设为空 | SetNull("dem" == -9999, "dem") |
这边要特别强调一个逻辑运算符的坑。在地图代数表达式里,逻辑与和逻辑或是用 & 和 |,不是 and 和 or。很多从Python转到ArcGIS栅格计算的朋友,第一反应写 ("a" > 1) and ("b" > 2),执行直接报错,因为栅格计算器的解析器里压根不认识 and 这个单词。正确的是 ("a" > 1) & ("b" > 2)。
再有一个就是运算符优先级。栅格计算器的优先级基本遵循数学直觉:括号最高,其次是指数运算符,然后是乘除,再是加减,最后是比较和逻辑运算符。但实际操作中,永远不要依赖默认优先级,确保每个逻辑组合都用括号明确表达。比如下面这两个表达式,看起来差不多,结果差很多:
text复制"dem" < 500 & "slope" < 10
按默认优先级,这个表达式实际执行的是 "dem" < (500 & "slope") < 10,完全不是你想的那个意思。而加了括号的版本才是你真正想要的双条件过滤:
text复制("dem" < 500) & ("slope" < 10)
2.3 命名规范与输出格式,最容易埋雷的地方
输出栅格路径这一栏看着不起眼,但它其实是报错率最高的区域。先说最常见的两种情况。
第一种情况,输出到地理数据库(Geodatabase,也就是个人地理数据库mdb或文件地理数据库gdb)。这时候输出名称不能以数字开头,不能带空格,不能包含特殊字符,比如你写 2024_result 会直接报错,因为名字以数字开头了。写成 result_2024 就没事。文件地理数据库对中文名称倒是兼容,但建议仍然用英文命名,避免在不同平台间转移数据时出现编码问题。
第二种情况,输出到普通文件夹。这种情况下必须在名称后面带上格式扩展名,比如 D:/work/result.tif 或 D:/work/result.img。如果不带扩展名,工具会提示“Invalid output location”或者直接生成失败。很多人第一次用栅格计算器,输出路径写了个 D:/work/result,结果一直报错找不出原因,其实就是少了 .tif 扩展名。
另外要留心的是,输出栅格的格式会直接影响后续能否被其他工具读取。.tif(GeoTIFF)是目前最通用、兼容性最好的格式,推荐优先选这个;.img 是ERDAS的格式,老项目里还在用;直接存到文件地理数据库里会生成GRID格式,但这个格式的文件是放在gdb目录下的,复制到别的电脑时容易漏文件。
3. 三个典型实操案例解析
3.1 案例一:基于DEM的高程筛选与二值化提取
场景描述:城市规划里要做生态红线初步划定,需要先找出高程低于800米的区域作为优先排查范围。手头有一份30米分辨率的DEM数据,名字叫 dem30。
打开栅格计算器,在表达式输入框里输入:
text复制Con("dem30" <= 800, 1, 0)
这里用Con函数而不是直接写 "dem30" <= 800,是因为Con可以同时控制真值和假值的具体数值。直接写比较表达式,输出结果的真值恒为1、假值恒为0,虽然也能用,但有时候我们希望在后续统计里使用特定的编码值,比如把符合区域标为1,不符合的标为NoData,方便后续直接做掩膜提取。这种情况下就可以配合SetNull函数:
text复制SetNull("dem30" > 800, "dem30")
这条表达式的含义是:大于800米的像元设为空值NoData,其余像元保留原始高程值。得到的结果再接其他工具做区域统计,就非常干净,因为NoData像元根本不会参与后续计算,不需要额外做掩膜处理。
实操中的小技巧是,在执行之前,先用Identify工具点几个已知的高程点验证一下DEM值的量级和单位。有的DEM高程单位是米,有的是英尺,还有的默认是厘米,如果单位搞错,800这个阈值选出来完全不是你想要的范围。我之前就吃过这个亏,某项目提供的数据文档里写的是米,实际值是厘米,导致整个筛选范围完全错掉,排查了半天才意识到是单位问题。
3.2 案例二:坡度+山体阴影的组合地形分析
场景描述:在某山区做通信基站选址,要求坡度不宜过大(< 25度),同时光照条件不能太差,需要综合地形的阴坡阳坡情况初步判断。这里的核心是先将DEM转换为坡度栅格和山体阴影栅格,再进行复合筛选。
第一步,先在栅格计算器里使用3D Analyst的地形函数生成派生图层。表达式如下:
text复制Slope("dem30")
执行后就会输出一个坡度栅格,单位是度(Degree)。同理可以生成山体阴影:
text复制Hillshade("dem30", 315, 45)
Hillshade函数接受三个参数,第一个是输入DEM,第二个是太阳方位角(Azimuth),默认是315度(西北方向),第三个是太阳高度角(Altitude),默认是45度。这两个参数会影响阴影渲染效果,但对分析来说更重要的是理解:山体阴影值越大代表光照越强,越接近255就是向阳面,越接近0就是阴影区。
第二步,执行组合筛选。将坡度条件与光照条件用逻辑与结合起来:
text复制Con((Slope("dem30") < 25) & (Hillshade("dem30", 315, 45) > 100), 1, 0)
这条表达式的执行过程是:先计算坡度栅格,再计算山体阴影栅格,然后逐像元判断“坡度小于25度”和“光照值大于100”是否同时满足。两个条件都满足的像元输出1,否则输出0。最终结果就是一张满足双重条件的候选区栅格图,叠加到地图上可以直接作为选址参考。
这个案例最大的价值在于,它展示了栅格计算器把多个派生数据和条件判断串联在一条表达式里的能力。中间产出的坡度栅格、山体阴影栅格都是临时数据,不需要单独保存,表达式执行完就释放了,整个流程既节省磁盘空间又提升处理效率。如果换用传统工具流,需要单独跑坡度工具保存中间结果,再跑山体阴影工具保存中间结果,最后再用条件判断工具做叠加,步骤明显更多。
3.3 案例三:Con函数实现多条件重分类
场景描述:做土地利用适宜性评价,需要按照高程、坡度、距河流距离三个因子的不同区间打权重分。三个栅格分别是:dem30(高程)、slope_deg(坡度,单位度)、dist_river(距河流距离,单位米)。打分规则如下:
- 高程小于500米打3分,500~1000米打2分,大于1000米打1分
- 坡度小于10度打3分,10~20度打2分,大于20度打1分
- 距河流距离小于200米打3分,200~500米打2分,大于500米打1分
用栅格计算器来实现这个打分逻辑,核心是嵌套Con函数:
text复制Con("dem30" < 500, 3, Con("dem30" <= 1000, 2, 1)) + Con("slope_deg" < 10, 3, Con("slope_deg" <= 20, 2, 1)) + Con("dist_river" < 200, 3, Con("dist_river" <= 500, 2, 1))
这条表达式一气呵成,三个因子分别打分后直接相加,得到的总分范围是3到9分。得分越高的区域,说明综合适宜性越好。
这里分享一个使用嵌套Con时的核心技巧:嵌套顺序最好从小到大或者从大到小保持单调。以内层条件 Con("dem30" <= 1000, 2, 1) 为例,当执行到这里时,说明外层条件 "dem30" < 500 已经不满足,即高程大于等于500米,所以内层判断只需要关注是否小于等于1000就行,逻辑是严密的。如果嵌套顺序乱跳,比如先判断大于1000、再判断小于500,逻辑会混乱且容易漏掉区间。
另外不得不提的是,多因子打分最后的结果栅格,数值范围可能受各因子权重影响产生偏移。如果后续需要把总分映射到0到100的标准化分值上,可以在表达式里再做一步线性拉伸:
text复制((Con(...) + Con(...) + Con(...)) - 3) / (9 - 3) * 100
这样输出的分值范围就是0到100,更便于成果图例的分级展示和报告解读。
4. 高频报错与排查心得实录
4.1 表达式无效,最常见的三类死法
“Invalid expression”应该是栅格计算器里出现频率最高的一条报错。碰到这个提示,不用慌,按下面三个方向逐层排查。
第一,检查图层名有没有带英文双引号。表达式里引用图层必须写成 "图层名",如果不带引号,计算器会把图层名当成一个函数或常量去解析,直接报错。图层列表里双击插入是标准操作,手写时把引号漏掉是最常见的低级错误。
第二,检查逻辑运算符是不是用错了。前面已经强调过,地图代数里逻辑与用 &、逻辑或用 |、逻辑非用 ~,不是Python里的 and、or、not。这几乎是新手必踩的坑,很多语法错误都源于此。
第三,检查表达式里有没有多余的空格或中文字符。运算符两边可以有空格,但图层名内部、函数名的拼写不能有中文字符。尤其是从Word或PDF文档里复制表达式时,引号容易被自动转换成中文全角引号“”,这种引号在计算器里无法识别,也是报错的常见原因。
如果是自己手敲的表达式报错,一个省事的排查办法是把表达式复制到记事本里,用显示空格和换行的模式检查一遍,几乎都能发现隐藏的全角字符或多余空格。
4.2 输出路径没反应,十有八九是命名格式的问题
点击OK之后工具没有反应,或者直接闪退,先别急着怀疑数据。这一步出现问题的概率,输出路径占八成。
输出到文件地理数据库(gdb)时,名称不要以数字开头,不能带空格。
输出到普通文件夹时,一定要带 .tif 或 .img 扩展名。
如果路径里包含中文目录,某些版本在输出时也会不稳定,建议路径里避免中文。
另一个很容易被忽略的点是,输出栅格不能与输入栅格同名。如果输入数据叫 dem30,输出也想叫 dem30,工具会拒绝执行,因为ArcGIS不允许输出覆盖输入。可以把输出名改成一个能体现处理内容的名字,比如 dem30_below800.tif,不仅避免冲突,还方便后续识别成果。
4.3 计算结果全为NoData或全为0,先检查范围和投影
计算结果一片全黑,或者全是空值,这个问题看起来吓人,但排查起来有固定套路。
先查数据范围。加载一张影像或一份行政区划矢量,跟结果图对比一下,如果结果栅格的范围明显比预期小很多,说明表达式计算时输入栅格的像元范围没有对齐。在ArcGIS里,多个栅格参与运算时,默认的像元大小和分析范围取第一个输入栅格的设置,如果叠加的时候没有统一设置环境参数,可能出现裁剪或对齐偏差。这时去工具菜单的“环境设置”里自定义处理范围(Processing Extent)和像元大小(Cell Size),全部设定为统一值再跑一次。
再查投影。参与计算的多个栅格如果坐标系不一致,比如一个是WGS84经纬度,一个是投影坐标系(如UTM或高斯克吕格),表达式的空间配准会失败,结果通常全是NoData。解决方法是先把所有栅格数据用Project Raster工具转换到同一个投影坐标系,再做计算。
还有一类隐蔽情况是NoData传播。只要输入栅格在某一个像元位置上是NoData,输出在这个位置也是NoData。如果DEM边缘有黑色块或数据空洞,计算结果自然会出现大范围的空值。可以用条件函数对NoData做补齐,比如:
text复制Con(IsNull("dem30"), 0, "dem30")
把空值位置补0后再参与后续计算,结果就不会出现大片空值了。
4.4 计算到一半内存不足,大数据量栅格的降载方案
栅格计算器在表达式执行前,会在内存里同时加载参与运算的所有栅格的全部像元。当数据量很大(比如全省范围的10米分辨率DEM),内存占用非常夸张,机器差一点就直接卡死或报“Insufficient Memory”错误。
解决思路有两个方向。一个思路是缩小计算范围,先对大数据栅格做裁剪或掩膜,只保留研究区范围再参与计算。另一个思路是降低数据量,在大范围计算时,优先使用30米分辨率的栅格,把10米的高分辨率数据先用Resample工具重采样到30米再参与计算。精度上会有轻微损失,但换来的计算稳定性在大多数分析场景里是完全值得的。
如果项目对分辨率有硬性要求,不接受重采样,还有一个更高效的办法是分块计算。把研究区按网格切开,每个网格单独用栅格计算器跑一遍,最后用Mosaic To New Raster工具拼回去。这个方法能有效规避单次运算内存不足的问题,但操作步骤要多一些,适合数据量特别大的场景。分块时不要忘了各分块之间留一点重叠区域,避免拼接后出现白缝。
5. 一些踩坑之后沉淀下来的实操心得
栅格计算器这个工具,入门门槛非常低,但要用好、用稳、不在关键时刻掉链子,有些习惯真的挺重要的。第一个习惯是,每次跑比较复杂的表达式之前,先用一个几秒钟就能出结果的小范围数据试运行一次,确认输出数值符合预期之后,再全量跑数据。这比表达式写错、全量计算跑了几小时之后才发现结果为空要省心得多。
第二个习惯是,重要的表达式记得在文本文档里留存一份。栅格计算器的表达式输入框没有历史记录功能,一旦关闭界面,之前写的表达式就找不回来了。我在项目里常备一个 raster_calc_formulas.txt 文件,把常用的Con嵌套模板、地形因子打分公式、SetNull处理空值的写法按项目分类存好,下次用到直接复制改参数就行。
还有一个值得专门说说的点,是栅格计算器跟Python窗口里使用Raster Calculator的差异。ArcGIS的Python窗口里,同样可以实现栅格计算,但语法变成了 arcpy.gp.RasterCalculator_sa() 或直接使用 Raster 对象和运算符重载。如果后续你的工作流要往自动化方向走,建议在学栅格计算器的同时,也把地图代数表达式和Python的Raster对象语法对照起来学。表达式 Con("dem30" < 500, 1, 0) 在Python里等价于:
python复制import arcpy
from arcpy.sa import *
arcpy.env.workspace = r"D:/work"
out_raster = Con(Raster("dem30") < 500, 1, 0)
out_raster.save("dem30_con.tif")
两种方式处理同一类问题,手工交互用栅格计算器,批量自动化用Python脚本,组合起来效率最高。
根据我个人经验,栅格计算器最值得花时间琢磨的部分不是那些复杂函数,而是Con函数的嵌套逻辑和条件组合,一旦把“逐像元判断”这个核心思维吃透了,面对任何栅格分析需求——不管是土地利用分类、淹没模拟还是地形适宜性评价——都能很快把分析思路转译成一条可执行的表达式。希望这篇教程能把你在栅格计算上的基础打扎实。
