ArcGIS栅格数据像元坐标与值提取导出Excel的实用指南
2026/9/16 20:46:33 网站建设 项目流程

1. 为什么要把栅格数据“拆”成行记录

1.1 这个需求从哪来

做GIS的人,几乎都遇到过这种情况:手头有一份tif栅格数据,可能是DEM高程、植被覆盖度、降水插值结果,也可能是夜光遥感反演出来的灯光指数。你想从中取特定位置的数值,或者想把每个像元的值和它的坐标一一对应地导出来,做成一份表格,交给不会用ArcGIS的同事做统计分析。

我自己的经历是在做植被覆盖度反演时,最终成果是一张NDVI分级栅格图,每个像元值代表覆盖度等级。客户看完图之后说:图先放一边,你把这个区域每个点的坐标和值给我列出来,我们自己建回归模型。这时候你在ArcMap里按“识别”工具一个一个点,显然不现实,一个像元一个像元去看,碰到大范围数据就是灾难。你需要的是批量提取、结构化输出的操作流程,最好能直接变成Excel表格。

这背后涉及的核心需求其实有三类,我替大家拆一下:

  1. 全量提取:把整个栅格全部像元的值+坐标导出,做分类统计、制表、机器学习样本构建;
  2. 定点采样:在若干已知点位上从栅格提取值,比如野外采样点对应的高程、土壤属性、温度;
  3. 批量处理:多幅栅格数据,要按相同的逻辑全部导成统一格式的表。

这篇文章就是围绕这三类需求展开的。不管你用的是ArcMap还是ArcGIS Pro,下面这套思路和方法都适用,核心工具和ArcPy脚本逻辑是共通的。

1.2 什么人适合读这篇文章

内容定位很明确:你手里有一张或多张栅格图,想快速提取“坐标+像元值”并导出成Excel。可能是做遥感、国土、林业、水利、环境方向的分析师,也可能是在做课程设计或毕业论文的同学,还可能是刚接触ArcGIS但被领导安排了个“像元提值”任务的职场新人。

这篇文章我会把三个层面的内容讲透:图形界面操作、工具参数背后的原理、ArcPy脚本批量方案。你只要掌握其中任何一种,就能覆盖日常90%以上的提值需求。我也不打算讲太多教科书理论,全程以实操为主,每个步骤都可以直接跟着做。

2. 动手前的概念准备:像元、坐标和NoData的底层关系

2.1 栅格数据不是图片,它自带地理空间标签

很多新手容易把tif当成普通图片,但实际上GIS里的tif除了每个格子的亮度值,还记录了坐标系统、像元大小、范围、旋转参数等等。换句话说,每个像元都不只是“第几行第几列”,它自带明确的地理位置。

打开ArcMap的图层属性再切到“源”选项卡,你能看到栅格的列数、行数、像元大小、空间参考、范围等信息。这些信息有什么用?举个例子,如果栅格是UTM投影坐标系,那每个像元中心点都可以用一个X坐标和一个Y坐标来表示,单位是米;如果栅格是WGS84地理坐标系,那坐标单位就是度。你在做提取的时候,ArcGIS会利用这些元数据自动算出每个像元中心点的位置,但你得搞清楚它算出来的坐标是在什么坐标系下。

有一个非常常见的问题:栅格本身是投影坐标系,但你想要的坐标是经纬度;或者反过来,栅格是经纬度,但你后续的空间分析需要投影坐标。正常拿到一份栅格数据,第一步不是急着提取值,而是先确认它的空间参考,再确定你要的输出坐标格式。

2.2 像元行号列号与真实坐标的换算逻辑

在一个不带旋转的标准栅格里,像元中心点的坐标换算其实很简单,公式如下:

X坐标 = 栅格左上角X + 列号 × 像元宽度 Y坐标 = 栅格左上角Y - 行号 × 像元高度

列号从0开始,行号从0开始,左上角第一个像元的中心点X坐标是left + 0.5个像元宽度,Y坐标是top - 0.5个像元高度。不过ArcGIS内部会处理好这个细节,你不需要自己加半个像元。

为什么要啰嗦这一句?因为在用ArcPy直接操作numpy数组时,数组中第0行第0列对应的是栅格左上角那个像元,而不是右下角。很多人在脚本里把Y方向搞反,导出的坐标在Y轴上整整差了一个范围,这是最典型的低级错误。

提示:如果你的栅格是旋转过的(比如用“世界文件”配准过的扫描图),上图公式就不适用了,需要使用仿射变换参数。但绝大多数遥感影像和GIS工具生成的栅格都不会有旋转问题,暂时可以不考虑。

2.3 别让NoData污染你的Excel

NoData是栅格数据里常见的“坑”。原始数据没有覆盖到的区域、遥感影像中的云遮挡像元、人为裁剪挖掉的范围,都会被赋予NoData。NoData的基本特征是:它不是0,也不是-9999,它是一个独立于像元值体系的“空”标记。

但问题在于,不同来源的栅格,NoData的编码不同。有的是-9999,有的是-3.4028235e+38,有的直接是0,有的甚至没有设置NoData就把某个数值当作无效值。如果你不提前处理,导出Excel后统计分析时就会出现大量莫名其妙的值。

我处理NoData的原则很简单:明确编码、统一处理、导出前过滤。具体做法后面会结合各个方法展开说明。

3. 方法一:“栅格转点”+“添加XY坐标”+“表转Excel”的通用链路

3.1 工具位置与参数含义

如果你需要把整幅栅格的所有像元全部转成点并导出,最直观的方法就是用“栅格转点”(Raster to Point)工具。

工具所在目录:

  • ArcMap:ArcToolbox → 转换工具 → 由栅格转出 → 栅格转点
  • ArcGIS Pro:工具箱 → 转换工具 → 由栅格转出 → 栅格转点

这个工具做的事情很纯粹:把每个栅格像元变成一个点要素,点的位置是像元中心点,点的属性表里有个字段叫Grid_code,记录该像元的像元值。

参数方面,主要需要设置的:

参数设置建议
输入栅格选择你的tif文件
字段默认选Value,保持默认
输出点要素指定一个输出要素类的完整路径

运行完工具后,一定要记住一点:虽然点要素已经带有了坐标位置,但它的属性表里并没有直接显示X和Y字段,你需要手动把坐标值写进属性表。

3.2 完整操作步骤:从tif到Excel

第一步:运行“栅格转点”,把栅格转成点要素。转完之后地图上会多出密密麻麻的点,这就是每个像元的位置。假如你的栅格像元数量上千,地图上显示可能正常;如果是百万级像元,地图加载会明显卡顿。

第二步:运行“添加XY坐标”(Add XY Coordinates)。工具位置:

  • 数据管理工具 → 要素 → 添加XY坐标

选中刚才生成的点的图层,运行。这时候打开属性表,你会看到POINT_X和POINT_Y两个字段,里面就是每个点对应的地图坐标。POINT_X是X坐标,POINT_Y是Y坐标。

第三步:打开属性表检查数据。重点检查Grid_code字段有没有异常值,确认POINT_X和POINT_Y的取值范围和栅格范围是否一致。

第四步:表转Excel。但注意,ArcMap中直接右键属性表选“导出”只能导出成DBF或文本文件,不能直接导出xls/xlsx。所以正确做法是用“表转Excel”工具:

  • ArcMap:转换工具 → Excel → 表转Excel
  • ArcGIS Pro:转换工具 → Excel → 表转Excel

输入要素选点的图层,输出路径指定一个xls或xlsx文件。运行结束后,Excel就在指定路径下生成了。

第五步:用Excel打开检查。如果文件太大,Excel打开会提示数据过多或格式损坏,这是正常的,可以通过后续的方法规避。

一句话总结整条链路:

栅格转点 → 添加XY坐标 → 表转Excel

这条链路很简单,但非常稳,适合中小数据量场景。

3.3 这个方法有什么限制

栅格转点的方案有一个比较大的问题:数据量膨胀。一个1000×1000的栅格转出来是100万个点,属性表100万行,转成Excel后文件体积很容易超过50MB,普通电脑打开都费劲。如果栅格本身是5000×5000,那就是2500万个点,ArcGIS处理起来基本处于“能用但很不舒服”的状态。

所以在方法一之前,建议先判断你的栅格像元规模。像元数在几十万级别,用方法一没问题;达到几百万甚至千万级别,直接考虑后面的ArcPy方案。

4. 方法二:“值提取到点”处理点采样场景

4.1 适用场景与核心区别

方法一适合全量提取,但很多场景下你并不需要把整个栅格全部转出来。举个例子:你有100个野外采样点的经纬度坐标,想从DEM高程栅格里提取每个点的高程值。这时候如果还用栅格转点,等于把整个栅格几百万个点全部导出,然后你再在里面筛选100个点,完全是在浪费时间和算力。

针对这种“点位采样”场景,用“值提取到点”(Extract Values to Points)工具更合适。它做的事情是:在每个点位上读取对应的栅格像元值,作为新字段写入点的属性表。输入点还是那些点,输出点还是那些点,只多了一个字段。

4.2 操作步骤与字段说明

第一步:准备点数据。如果你手里是坐标表,可以先用“添加XY数据”功能把表变成点图层。操作位置:

  • ArcMap:文件 → 添加数据 → 添加XY数据
  • ArcGIS Pro:地图选项卡 → 添加数据 → XY表转点

第二步:打开“值提取到点”工具。

  • ArcMap:ArcToolbox → 空间分析工具 → 提取分析 → 值提取到点
  • ArcGIS Pro:工具箱 → 空间分析工具 → 提取分析 → 值提取到点

第三步:设置参数。输入点要素选你的采样点图层,输入栅格选目标栅格,输出点要素指定输出位置。运行。

第四步:打开输出图层的属性表,会看到一个字段叫RASTERVALU,这就是每个点所在位置对应的栅格值。

第五步:和之前一样,跑一下“添加XY坐标”,把坐标字段加进去,再“表转Excel”导出。

4.3 一个很容易搞混的参数:插值选项

“值提取到点”工具有个参数叫“插值选项”(Interpolate values),默认不勾选。很多人搞不清这个选项是干嘛的。

不勾选:工具直接取该点所在像元的原始像元值,快速但不够精细; 勾选:工具会用双线性插值法,把点周围的像元值做加权平均,得到更平滑的结果。

怎么选?如果栅格分辨率比较粗,点又恰好落在像元边缘,勾选插值能得到更合理的估值;如果你希望提取的结果就是原始数据本身,比如构建机器学习样本时不想引入插值误差,那就保持默认不勾选。我个人大部分场景都不勾选,理由很简单:RASTERVALU应该是栅格在该点的真实值,插值相当于人为改变了原始信息,后续模型使用时反而容易出问题。

5. 方法三:ArcPy脚本批量提取,效率提升的核心方案

5.1 为什么需要脚本化

前两种方法对付中小数据量和单栅格场景绰绰有余。但一旦遇到下面这些场景,图形界面就力不从心了:

  • 栅格像元数量在千万级以上,栅格转点整个界面都会卡顿;
  • 有几十幅栅格需要做同样操作,手工一个个点工具、一个个导出,耗时长且容易漏;
  • 数据更新频繁,每周都要从新出的栅格里提取值做报表;
  • 需要把提取结果直接接进后续Python数据分析流程,而不是先导出Excel再重新读一遍。

ArcPy的核心优势在于,它能直接用numpy数组读写栅格数据,绕开了ArcGIS图形界面在渲染和显示上的负担。配合pandas,提取几百万像元的值也只需要几秒钟。

5.2 核心脚本:把栅格转成坐标+值表格

下面这段是我日常使用频率最高的脚本,作用是读一个栅格,把每个有效像元的X坐标、Y坐标和值组成一张表,导出为Excel。

import arcpy import numpy as np import pandas as pd # 设置工作环境 arcpy.env.workspace = r"D:\gis_data" arcpy.env.overwriteOutput = True # 栅格路径 raster_path = r"D:\gis_data\dem.tif" # 读取栅格,NoData统一转成NaN raster = arcpy.Raster(raster_path) arr = arcpy.RasterToNumPyArray(raster, nodata_to_value=np.nan) # 获取空间参数 extent = raster.extent cell_x = raster.meanCellWidth cell_y = raster.meanCellHeight left = extent.XMin top = extent.YMax rows, cols = arr.shape # 生成每个像元的中心点坐标矩阵 x_coords = left + np.arange(cols) * cell_x y_coords = top - np.arange(rows) * cell_y X, Y = np.meshgrid(x_coords, y_coords) # 过滤NoData valid_mask = ~np.isnan(arr) # 组装DataFrame df = pd.DataFrame({ "X": X[valid_mask], "Y": Y[valid_mask], "Value": arr[valid_mask] }) # 导出Excel df.to_excel(r"D:\gis_data\dem_values.xlsx", index=False) print("完成,共", len(df), "条记录")

这段脚本我拆开解释一下:

  • RasterToNumPyArray把栅格读取为numpy二维数组,nodata_to_value=np.nan把所有NoData统一转成NaN,后面过滤就很方便;
  • meanCellWidthmeanCellHeight是像元的宽度和高度,对绝大多数标准栅格来说,这俩值就是一个正的cell size和一个同样大小的负值(因为Y方向坐标是递减的);
  • np.meshgrid生成了所有像元中心点的X坐标矩阵和Y坐标矩阵;
  • valid_mask是布尔型数组,True的位置表示该像元有效,用它同时索引X、Y和值,提取出来的数据就是一一对应的。

不用for循环遍历像元,直接用numpy矢量化操作,处理速度会快很多。我试过在普通办公电脑上处理2000×2000的栅格,也就是400万个像元,整个流程在5秒以内完成。

5.3 批量处理多幅栅格

如果你有多幅栅格需要批量导出,只需在外层套一个循环,用arcpy.ListRasters()扫描工作空间下的所有栅格文件:

import arcpy import numpy as np import pandas as pd import os arcpy.env.workspace = r"D:\gis_data\rasters" arcpy.env.overwriteOutput = True out_dir = r"D:\gis_data\output" if not os.path.exists(out_dir): os.makedirs(out_dir) raster_list = arcpy.ListRasters() for raster_name in raster_list: raster_path = os.path.join(arcpy.env.workspace, raster_name) raster = arcpy.Raster(raster_path) arr = arcpy.RasterToNumPyArray(raster, nodata_to_value=np.nan) extent = raster.extent cell_x = raster.meanCellWidth cell_y = raster.meanCellHeight left = extent.XMin top = extent.YMax rows, cols = arr.shape x_coords = left + np.arange(cols) * cell_x y_coords = top - np.arange(rows) * cell_y X, Y = np.meshgrid(x_coords, y_coords) valid_mask = ~np.isnan(arr) df = pd.DataFrame({ "X": X[valid_mask], "Y": Y[valid_mask], "Value": arr[valid_mask] }) out_file = os.path.join(out_dir, raster_name.replace(".tif", "") + "_values.xlsx") df.to_excel(out_file, index=False) print(raster_name, "完成,共", len(df), "条记录")

批量循环里我习惯加一个os.makedirs判断,避免输出目录不存在时报错。另外建议在输出文件名里保留原始栅格名,不然多个栅格导出的Excel文件会互相覆盖。

5.4 多波段栅格怎么提指定的波段

如果你的栅格是多波段的(比如遥感影像有R、G、B、NIR四个波段),上面脚本默认提取的是第一个波段。要提取指定波段,用arcpy.Raster(raster_path + "/Band_3")这类带波段索引的路径。

raster = arcpy.Raster(r"D:\gis_data\ndvi.tif/Band_2")

如果你的栅格没有波段名,只按数字索引,也可以:

raster = arcpy.Raster(r"D:\gis_data\影像.tif/1")

但说实话,多波段栅格我建议先用“栅格计算器”或“提取波段工具”把目标波段单独拆出来,再进脚本逻辑。这样波段拆分和提值两步分开,排查问题更容易。

6. 常见问题、报错与我的处理经验

6.1 导出的坐标和预期不符,差得离谱

这个问题的典型表现是:你导出的X坐标是七位数,Y坐标是八位数,但你本来想要的是经纬度(度),或者反过来,你想要投影坐标却导出的是经纬度。

核心原因在于:栅格本身是什么坐标系,提取出的坐标就是什么坐标系,工具不会自动帮你转换。解决方法有两个:

方案一:提取前改栅格坐标。用“投影栅格”(Project Raster)工具,把栅格从当前坐标系转到目标坐标系,再执行提取。优点是后续所有步骤都在目标坐标系下进行,坐标直接是你要的结果。

方案二:提取后用“投影”工具转换点要素。如果你已经有了点图层或Excel坐标字段,可以用“投影”工具把点要素转到目标坐标系,再重新“添加XY坐标”。

我个人的习惯是方案一。提值前就把坐标系统一整明白,比事后反复转换要省事很多。

6.2 大栅格导出卡死

用“栅格转点”导出大栅格,ArcMap很容易内存溢出或长时间无响应。这里有几个实用的降级策略:

  • 如果只是分析某个小区域,先用“按掩膜提取”或“裁剪栅格”把范围缩小;
  • 使用“重采样”工具把像元变大,比如从10米重采样到30米,像元数量直接变为原来的1/9;
  • 用ArcPy方案替代图形界面,numpy处理大数组比ArcGIS对象高效得多;
  • 真到了几千万行Excel的规模,别导出xlsx了,直接导出CSV。CSV文件轻量得多,pandas和Excel打开都更快。
df.to_csv(r"D:\gis_data\dem_values.csv", index=False, encoding="utf-8-sig")

这里我用了utf-8-sig,是因为Excel打开CSV时如果文件是UTF-8编码但没加BOM,中文表头会变成乱码。

6.3 NoData被当成正常数值导出

同样的NoData问题,图形界面和脚本里都有可能遇到。图形界面的情况是栅格属性里NoData被设成了某个具体数值,比如-9999,而ArcGIS没有识别出来;脚本里的情况是nodata_to_value参数设置不当。

解决办法是提前清洗栅格:

  • 用“复制栅格”工具重新写一份,把NoData值设为NONE;
  • 或者用“栅格计算器”把指定值变成NoData:
SetNull("dem.tif" == -9999, "dem.tif")
  • 在ArcPy脚本里,如果NoData是-9999而不是NaN,可以先转换成NaN再过滤:
arr[arr == -9999] = np.nan

6.4 属性表里字段太多,Excel列乱了

用“表转Excel”导出时,如果点图层的属性表带了很多用不上的字段(比如原有的ID、Area、Perimeter字段),导出的Excel列会很乱。处理方式有两种:

  • 导出前,在属性表里右键删除不需要的字段;
  • 用ArcPy脚本时,直接指定要输出的列,根本没机会带多余字段。

我更建议后者,因为脚本指定列的方式更可控,改起来也方便。

7. 进阶:提取结果统计分析

导出Excel只是第一步,很多人拿到数据后要做统计分析。这里分享一个把提取结果和pandas统计结合的小例子。

import pandas as pd df = pd.read_excel(r"D:\gis_data\dem_values.xlsx") # 查看基本统计量 print(df["Value"].describe()) # 按值域分段统计数量 bins = [0, 10, 20, 30, 40, 50, df["Value"].max()] df["class"] = pd.cut(df["Value"], bins=bins) print(df["class"].value_counts()) # 按格网划分统计均值(比如把X、Y按10万间隔分组) df["x_grid"] = (df["X"] // 100000) * 100000 df["y_grid"] = (df["Y"] // 100000) * 100000 grid_result = df.groupby(["x_grid", "y_grid"])["Value"].mean().reset_index() print(grid_result.head())

如果只是想知道某个研究区域里像元值的最大值、最小值、平均值,其实不需要导Excel,直接用“分区统计”(Zonal Statistics as Table)工具更快,它会把每个面要素范围内的栅格统计值直接输出成属性表。

真正的流程选择逻辑我总结成了这样一张表:

需求场景首选方案备选方案
整幅栅格全部像元导出ArcPy脚本批量处理栅格转点+表转Excel
特定点位提取栅格值值提取到点ArcPy采样
按面要素统计区域值分区统计栅格转点+空间连接
多波段影像提单波段提取波段+ArcPy栅格计算器
超大栅格重采样或裁剪后导出CSV分块处理

8. 最后分享两个小技巧

第一件事,无论用哪个方法,导出完成后务必回到ArcMap里抽几个点做核验。怎么核验?用“识别”工具,在地图上点同一位置看栅格像元值,再和你导出的Excel对应行的Value比对。数值对上了,坐标再核对一下,没什么问题了,这份表格才算真正可用。我以前就因为没核验坐标,直接拿导出的点去做缓冲,跑到外面才发现偏了一千米,回去排查,是源栅格的坐标系和底图坐标系不一致导致的,白折腾了一个下午。

第二件事,ArcPy脚本里建议强制设置arcpy.env.overwriteOutput = True,否则你重复运行脚本时,ArcGIS会因为输出文件已存在直接报错。很多人在脚本里反复运行不通过,都是被这个默认的false给挡了。

这个内容后续还可以往两个方向扩展:一是把提取流程做成ArcGIS Pro的Python工具箱(.pyt),这样其他同事不用打开代码就能在工具箱界面里使用;二是在脚本里接上后续的数据分析和可视化步骤,直接生成统计图表,让整个流程从栅格输入到成果输出一步到位。等你把这套流程跑顺了,相信你也会有自己的优化想法。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询