
1. 为什么要把栅格数据“拆”成行记录1.1 这个需求从哪来做GIS的人几乎都遇到过这种情况手头有一份tif栅格数据可能是DEM高程、植被覆盖度、降水插值结果也可能是夜光遥感反演出来的灯光指数。你想从中取特定位置的数值或者想把每个像元的值和它的坐标一一对应地导出来做成一份表格交给不会用ArcGIS的同事做统计分析。我自己的经历是在做植被覆盖度反演时最终成果是一张NDVI分级栅格图每个像元值代表覆盖度等级。客户看完图之后说图先放一边你把这个区域每个点的坐标和值给我列出来我们自己建回归模型。这时候你在ArcMap里按“识别”工具一个一个点显然不现实一个像元一个像元去看碰到大范围数据就是灾难。你需要的是批量提取、结构化输出的操作流程最好能直接变成Excel表格。这背后涉及的核心需求其实有三类我替大家拆一下全量提取把整个栅格全部像元的值坐标导出做分类统计、制表、机器学习样本构建定点采样在若干已知点位上从栅格提取值比如野外采样点对应的高程、土壤属性、温度批量处理多幅栅格数据要按相同的逻辑全部导成统一格式的表。这篇文章就是围绕这三类需求展开的。不管你用的是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污染你的ExcelNoData是栅格数据里常见的“坑”。原始数据没有覆盖到的区域、遥感影像中的云遮挡像元、人为裁剪挖掉的范围都会被赋予NoData。NoData的基本特征是它不是0也不是-9999它是一个独立于像元值体系的“空”标记。但问题在于不同来源的栅格NoData的编码不同。有的是-9999有的是-3.4028235e38有的直接是0有的甚至没有设置NoData就把某个数值当作无效值。如果你不提前处理导出Excel后统计分析时就会出现大量莫名其妙的值。我处理NoData的原则很简单明确编码、统一处理、导出前过滤。具体做法后面会结合各个方法展开说明。3. 方法一“栅格转点”“添加XY坐标”“表转Excel”的通用链路3.1 工具位置与参数含义如果你需要把整幅栅格的所有像元全部转成点并导出最直观的方法就是用“栅格转点”Raster to Point工具。工具所在目录ArcMapArcToolbox → 转换工具 → 由栅格转出 → 栅格转点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 → 表转ExcelArcGIS 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表转点第二步打开“值提取到点”工具。ArcMapArcToolbox → 空间分析工具 → 提取分析 → 值提取到点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 rD:\gis_data arcpy.env.overwriteOutput True # 栅格路径 raster_path rD:\gis_data\dem.tif # 读取栅格NoData统一转成NaN raster arcpy.Raster(raster_path) arr arcpy.RasterToNumPyArray(raster, nodata_to_valuenp.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(rD:\gis_data\dem_values.xlsx, indexFalse) print(完成共, len(df), 条记录)这段脚本我拆开解释一下RasterToNumPyArray把栅格读取为numpy二维数组nodata_to_valuenp.nan把所有NoData统一转成NaN后面过滤就很方便meanCellWidth和meanCellHeight是像元的宽度和高度对绝大多数标准栅格来说这俩值就是一个正的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 rD:\gis_data\rasters arcpy.env.overwriteOutput True out_dir rD:\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_valuenp.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, indexFalse) print(raster_name, 完成共, len(df), 条记录)批量循环里我习惯加一个os.makedirs判断避免输出目录不存在时报错。另外建议在输出文件名里保留原始栅格名不然多个栅格导出的Excel文件会互相覆盖。5.4 多波段栅格怎么提指定的波段如果你的栅格是多波段的比如遥感影像有R、G、B、NIR四个波段上面脚本默认提取的是第一个波段。要提取指定波段用arcpy.Raster(raster_path /Band_3)这类带波段索引的路径。raster arcpy.Raster(rD:\gis_data\ndvi.tif/Band_2)如果你的栅格没有波段名只按数字索引也可以raster arcpy.Raster(rD:\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(rD:\gis_data\dem_values.csv, indexFalse, encodingutf-8-sig)这里我用了utf-8-sig是因为Excel打开CSV时如果文件是UTF-8编码但没加BOM中文表头会变成乱码。6.3 NoData被当成正常数值导出同样的NoData问题图形界面和脚本里都有可能遇到。图形界面的情况是栅格属性里NoData被设成了某个具体数值比如-9999而ArcGIS没有识别出来脚本里的情况是nodata_to_value参数设置不当。解决办法是提前清洗栅格用“复制栅格”工具重新写一份把NoData值设为NONE或者用“栅格计算器”把指定值变成NoDataSetNull(dem.tif -9999, dem.tif)在ArcPy脚本里如果NoData是-9999而不是NaN可以先转换成NaN再过滤arr[arr -9999] np.nan6.4 属性表里字段太多Excel列乱了用“表转Excel”导出时如果点图层的属性表带了很多用不上的字段比如原有的ID、Area、Perimeter字段导出的Excel列会很乱。处理方式有两种导出前在属性表里右键删除不需要的字段用ArcPy脚本时直接指定要输出的列根本没机会带多余字段。我更建议后者因为脚本指定列的方式更可控改起来也方便。7. 进阶提取结果统计分析导出Excel只是第一步很多人拿到数据后要做统计分析。这里分享一个把提取结果和pandas统计结合的小例子。import pandas as pd df pd.read_excel(rD:\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], binsbins) 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这样其他同事不用打开代码就能在工具箱界面里使用二是在脚本里接上后续的数据分析和可视化步骤直接生成统计图表让整个流程从栅格输入到成果输出一步到位。等你把这套流程跑顺了相信你也会有自己的优化想法。