如何使用Python获取栅格中每个像素的XY坐标和像元值?


16

我是Python的新手,我想知道是否存在一种快速方法来在ArcGIS 10中使用Python逐个像素获取栅格像元值以及坐标(每个像素中心的地图XY坐标)?

为了进一步描述这一点,我需要获取第一个像素的地图X,地图Y和像元值,并将这三个值分配给三个变量,并对其余的其他像素重复此步骤(遍历整个栅格)。


我想我需要更多地描述我的问题。问题是,我需要获取第一个栅格的像素的XY位置,并获取与该XY位置相对应的其他几个栅格的像元值。此过程应遍历第一个栅格的每个像素,而无需创建任何中间点shapefile,因为这将真的非常耗时,因为我必须处理具有近80亿像素的栅格。另外,我需要在ArcGIS 10中使用Python进行此操作。

@JamesS:非常感谢您的建议。是的,这将适用于一个栅格,但是我还需要收集其他几个栅格的像元值。问题是,在获取第一栅格的第一个像素的X和Y坐标之后,我需要获取第二栅格的像元值,该值对应于第一栅格的X,Y位置,然后是第三栅格,依此类推。因此,我认为在遍历第一个栅格时,应该同时完成像素的X和Y位置以及与该位置相对应的其他栅格的像元值的获取,但我不确定。这可以通过将第一个栅格转换为点shapefile并在ArcGIS 10中执行将多值提取为点功能来完成,但是我

@hmfly:谢谢,是的,如果我可以获取数组的已知行和列值的坐标,则此方法(RastertoNumpyarray)将起作用。

@whuber:我不想执行任何计算,我要做的就是将XY坐标和单元格值写入文本文件,仅此而已


也许您只想对整个栅格进行一些数学运算?栅格计算器逐像素工作。
BWill's

1
请更详细地描述您的目的。
BWill's

通常,通过使用地图代数运算而不是在点上循环即可获得有效且可靠的解决方案。Spatial Analyst的地图代数实现的局限性阻止了这种方法在每种情况下的工作,但是在出乎意料的大量情况下,您不必编写循环。您到底需要执行什么计算?
whuber

重新编辑:这当然是合法目的。进一步的软件需求可能会给您带来这种格式。但是考虑到写入80亿(X,Y,value1,...,value3)元组将需要2240亿字节(二进制)到4000亿字节(ASCII),这两个都是很大的数据集,也许值得为您最终试图实现的目标找到替代方法!
ub

Answers:


11

遵循@Dango的想法,我创建并测试了(在具有相同范围和像元大小的小型栅格上)以下代码:

import arcpy, numpy

inRaster = r"C:\tmp\RastersArray.gdb\InRaster"
inRaster2 = r"C:\tmp\RastersArray.gdb\InRaster2"

##Get properties of the input raster
inRasterDesc = arcpy.Describe(inRaster)

#coordinates of the lower left corner
rasXmin = inRasterDesc.Extent.Xmin
rasYmin = inRasterDesc.Extent.Ymin

# Cell size, raster size
rasMeanCellHeight = inRasterDesc.MeanCellHeight
rasMeanCellWidth = inRasterDesc.MeanCellWidth
rasHeight = inRasterDesc.Height
rasWidth = inRasterDesc.Width

##Calculate coordinates basing on raster properties
#create numpy array of coordinates of cell centroids
def rasCentrX(rasHeight, rasWidth):
    coordX = rasXmin + (0.5*rasMeanCellWidth + rasWidth)
    return coordX
inRasterCoordX = numpy.fromfunction(rasCentrX, (rasHeight,rasWidth)) #numpy array of X coord

def rasCentrY(rasHeight, rasWidth):
    coordY = rasYmin + (0.5*rasMeanCellHeight + rasHeight)
    return coordY
inRasterCoordY = numpy.fromfunction(rasCentrY, (rasHeight,rasWidth)) #numpy array of Y coord

#combine arrays of coordinates (although array for Y is before X, dstack produces [X, Y] pairs)
inRasterCoordinates = numpy.dstack((inRasterCoordY,inRasterCoordX))


##Raster conversion to NumPy Array
#create NumPy array from input rasters 
inRasterArrayTopLeft = arcpy.RasterToNumPyArray(inRaster)
inRasterArrayTopLeft2 = arcpy.RasterToNumPyArray(inRaster2)

#flip array upside down - then lower left corner cells has the same index as cells in coordinates array
inRasterArray = numpy.flipud(inRasterArrayTopLeft)
inRasterArray2 = numpy.flipud(inRasterArrayTopLeft2)


# combine coordinates and value
inRasterFullArray = numpy.dstack((inRasterCoordinates, inRasterArray.T))

#add values from second raster
rasterValuesArray = numpy.dstack((inRasterFullArray, inRasterArray2.T))

基于@hmfly代码,您可以访问所需的值:

(height, width, dim )=rasterValuesArray.shape
for row in range(0,height):
    for col in range(0,width):
        #now you have access to single array of values for one cell location

不幸的是,只有一个“但是”-该代码适用于可以由系统内存处理的NumPy数组。对于我的系统(8GB),最大的阵列约为9000,9000。

由于我的经验不允许我提供更多帮助,因此您可以考虑一些有关处理大型数组的建议:https : //stackoverflow.com/questions/1053928/python-numpy-very-large-matrices

arcpy.RasterToNumPyArray方法允许指定转换为NumPy数组的栅格子集(ArcGIS10帮助页面),在将大型数据集分块为子矩阵时,这很有用。


Marcin的代码超级好!谢谢,但是它没有以相同的栅格分辨率写入栅格的X,Y,我的意思是x和y会增长1 m,而不是100米。例如,...谢谢你

7

如果只想通过(行,列)获取像素值,则可以编写如下所示的arcpy脚本:

import arcpy
raster = arcpy.Raster("yourfilepath")
array = arcpy.RasterToNumPyArray(raster)
(height, width)=array.shape
for row in range(0,height):
    for col in range(0,width):
        print str(row)+","+str(col)+":"+str(array.item(row,col))

但是,如果您想获取像素的坐标,则NumPyArray无法帮助您。您可以使用RasterToPoint工具将栅格转换为点,然后通过Shape字段获取坐标。


7

在ArcGIS 10中将坐标和单元格值输出到文本文件的最简单方法是示例函数,不需要代码,尤其是不需要遍历每个单元格。在ArcGIS <= 9.3x栅格计算器中,它曾经像outfile.csv = sample(someraster)输出所有(非null)像元值和坐标(z,x,y格式)的文本文件一样简单。在ArcGIS 10中,“ in_location_data”参数现在是必需参数,因此您需要使用语法Sample(someraster, someraster, outcsvfile)

编辑:您还可以指定多个栅格:Sample([someraster, anotherraster, etc], someraster, outcsvfile)。这是否适用于80亿个细胞,我不知道...

编辑:注意,我尚未在ArcGIS 10中对此进行过测试,但是多年来在<= 9.3(和工作站)中使用了示例函数。

编辑:我现在已经在ArcGIS 10中进行了测试,它不会输出到文本文件。该工具自动将文件扩展名更改为“ .dbf”。但是...以下Python代码可以正常工作,因为ArcGIS 10仍支持SOMA和MOMA地图代数语句:

import arcgisscripting
gp=arcgisscripting.create()
gp.multioutputmapalgebra(r'%s=sample(%s)' % (outputcsv,inputraster))

非常好。感谢您指出这一点-我以前没有注意到这个工具。当然比我的解决方案更加整洁和简单!
JamesS 2012年

6

一种方法是使用Raster_To_Point工具,然后再使用Add_XY_Coordinates工具。您将得到一个shapefile,其中属性表中的每一行代表栅格中的一个像素,其中包含X_CoordY_CoordCell_Value的列。然后,您可以使用游标在此表上循环(或根据需要将其导出到类似Excel的文件中)。

如果仅要处理一个栅格,则可能不值得编写脚本-只需使用ArcToolbox中的工具即可。如果您需要对许多栅格执行此操作,则可以尝试如下操作:

[ 注意:我没有ArcGIS 10,也不熟悉ArcPy,所以这只是一个非常粗糙的轮廓。它未经测试,几乎肯定需要进行调整才能使其正常工作。]

import arcpy, os
from arcpy import env

# User input
ras_fold = r'path/to/my/data'           # The folder containing the rasters
out_fold = r'path/to/output/shapefiles' # The folder in which to create the shapefiles

# Set the workspace
env.workspace = ras_fold

# Get a list of raster datasets in the raster folder
raster_list = arcpy.ListRasters("*", "All")

# Loop over the rasters
for raster in raster_list:
    # Get the name of the raster dataset without the file extension
    dataset_name = os.path.splitext(raster)[0]

    # Build a path for the output shapefile
    shp_path = os.path.join(out_fold, '%s.shp' % dataset_name)

    # Convert the raster to a point shapefile
    arcpy.RasterToPoint_conversion(raster, shp_path, "VALUE")

    # Add columns to the shapefile containing the X and Y co-ordinates
    arcpy.AddXY_management(shp_path)

然后,您可以使用搜索光标或(可能更简单)使用dbfpy遍历 shapefile属性表。这将允许您从栅格(现在存储在shapefile .dbf表中)中读取数据到python变量中。

from dbfpy import dbf

# Path to shapefile .dbf
dbf_path = r'path\to\my\dbf_file.dbf'

# Open the dbf file
db = dbf.Dbf(dbf_path)

# Loop over the records
for rec in db:
    cell_no = rec['POINTID'] # Numbered from top left, running left to right along each row
    cell_x = rec['POINT_X']
    cell_y = rec['POINT_Y']
    cell_val = rec['GRID_CODE']

    # Print values
    print cell_no, cell_x, cell_y, cell_val

3

也许您可以为栅格创建一个世界文件,将栅格隐藏为一个numpy数组。然后,如果您遍历数组,您将获得单元格值,并且如果从世界文件中不进行更新,则您还将具有每个单元格值的坐标。希望是有用的。


如果您对JamesS建议的“栅格转点”工具方法不感兴趣,我想这就是方法。
nmpeterson 2012年

3

Marcin的代码工作正常,除了rasCentrX和rasCentrY函数中的问题导致输出坐标以不同的分辨率出现(如Grazia所观察到的)。我的解决办法是改变

coordX = rasXmin + (0.5*rasMeanCellWidth + rasWidth)

coordX = rasXmin + ((0.5 + rasWidth) * rasMeanCellWidth)

  coordY = rasYmin + (0.5*rasMeanCellHeight + rasHeight)

  coordY = rasYmin + ((0.5 + rasHeight) * rasMeanCellHeight)

我使用代码将ESRI网格转换为CSV文件。这是通过删除对inRaster2的引用,然后使用csv.writer输出坐标和值来实现的:

out = csv.writer(open(outputCSV,"wb"), delimiter=',', quoting=csv.QUOTE_NONNUMERIC)
out.writerow(['X','Y','Value'])
(height, width, dim )=inRasterFullArray.shape
for row in range(0,height):
    for col in range(0,width):
        out.writerow(inRasterFullArray[row,col])

我也没有发现需要转置

inRasterFullArray = numpy.dstack((inRasterCoordinates, inRasterArray.T))

所以转换成

inRasterFullArray = numpy.dstack((inRasterCoordinates, inRasterArray))

2

丑陋但高效:

  1. 创建一个新的点要素,其中有4个点位于所关注的栅格角之外。确保与所讨论的栅格位于同一坐标系中。
  2. 添加“ xcor”和“ ycor”双字段
  3. 计算几何以获得这些字段的坐标
  4. 空间分析师->插值-> 趋势 -> 线性回归
  5. 环境设置:将栅格和像元大小与所讨论的栅格对齐
  6. 分别执行“ xcor”和“ ycor”
  7. 评分者以坐标作为单元格值,用作脚本的输入。

2

使用开源python包的简单解决方案:

import fiona
import rasterio
from pprint import pprint


def raster_point_coords(raster, points):

    # initialize dict to hold data
    pt_data = {}

    with fiona.open(points, 'r') as src:
        for feature in src:
            # create dict entry for each feature
            pt_data[feature['id']] = feature

    with rasterio.open(raster, 'r') as src:
        # read raster into numpy array
        arr = src.read()
        # rasterio always reads into 3d array, this is 2d, so reshape
        arr = arr.reshape(arr.shape[1], arr.shape[2])
        # get affine, i.e. data needed to work between 'image' and 'raster' coords
        a = src.affine

    for key, val in pt_data.items():
        # get coordinates
        x, y = val['geometry']['coordinates'][0], val['geometry']['coordinates'][1]
        # use affine to convert to row, column
        col, row = ~a * (x, y)
        # remember numpy array is indexed array[row, column] ie. y, x
        val['raster_value'] = arr[int(row), int(col)]

    pprint(pt_data) 

if __name__ == '__main__':
    # my Landsat raster
    ras = '/data01/images/sandbox/LT05_040028_B1.tif'
    # my shapefile with two points which overlap raster area
    pts = '/data01/images/sandbox/points.shp'
    # call function
    raster_point_coords(ras, pts)

Fiona十分方便,因为您可以打开shapefile,遍历要素,并将其附加到dict对象上。确实,Fiona feature本身也是dict如此,因此很容易访问属性。如果我的点具有任何属性,它们将与坐标,id等一起出现在此字典中。

Rasterio方便使用,因为它很容易以numpy数组,轻量且快速的数据类型在栅格中读取。我们还可以访问dict包括的栅格属性affine,这是将栅格x,y坐标转换为数组row,col坐标所需的所有数据。见@ perrygeo出色的解释在这里

我们最终得到一个pt_data类型dict,该类型具有每个点的数据并提取了raster_value。如果需要,我们也可以轻松地使用提取的数据重写shapefile。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.