Un recorrido rápido por el uso de Iterador de celda ráster

Puede utilizar el Raster Cell Iterator (RCI) para realizar un análisis de ráster personalizado. Permite visitar ubicaciones individuales de celda ráster y ofrece control sobre la iteración para consultar y modificar valores de celda ráster, todo en un entorno de Python.

Operaciones básicas de Iterador de celda ráster

En esta sección, obtendrá una comprensión conceptual de las operaciones básicas que puede realizar con RCI. Aprenderá a: crear un RCI implícito, consultar y asignar valores a celdas ráster, crear un ráster vacío con metadatos, indexar un objeto ráster y administrar NoData. Cada sección incluye ejemplos de código que muestran cómo utilizar RCI desde una ventana de Python. Se recomienda que lea este documento de arriba abajo para comprender estos ejemplos de código.

Iterador de celda ráster implícito

Es posible invocar un RCI en un objeto ráster de manera implícita para iterar por los índices de fila y columna de un ráster. El ejemplo de código que se muestra a continuación crea un objeto ráster denominado myRas desde un dataset ráster existente "myras". Se define un iterador ráster implícito en el objeto ráster que enumera pares de índice de fila y columna a lo largo del ráster en un bucle. Dentro del iterador, el índice de fila i, el índice de columna j y el valor de celda myRas[i,j] se imprimen en cada ubicación de celda. Este ejemplo muestra la consulta de un valor de celda en una ubicación determinada con notación de índice.

from arcpy.sa import *
myRas = Raster("myras")
for i,j in myRas:
    print(i, j, myRas[i,j])

Asignar valores a celdas

Gracias a RCI, puede asignar valores a celdas ráster y, por lo tanto, modificar el dataset ráster. De forma predeterminada, los objetos ráster creados desde un dataset ráster existente tienen una propiedad readOnly definida como True. Para modificar datasets ráster, la propiedad readOnly primero se debe definir como False. Una vez modificadas las celdas ráster, se debería recurrir al método save() en el objeto ráster para conservar estos cambios. Para obtener más información sobre la propiedad readOnly y el guardado de rásteres, consulte el tema de ayuda del objeto Raster.

myRas.readOnly = False
for i,j in myRas:
    if myRas[i,j] == 0:
        myRas[i,j] = 128
myRas.save()

Crear rásteres vacíos

El método getRasterInfo() le permite crear fácilmente un objeto ráster vacío y copiar metadatos desde un objeto ráster existente. Se recurre al método getRasterInfo() en un objeto ráster existente, lo que devuelve un objeto rasterInfo() para este ráster. Después, el objeto rasterInfo() se transmite como parámetro de entrada a la clase Raster() para crear una instancia de un ráster vacío. El objeto de ráster vacío tiene los mismos metadatos que el objeto ráster del que procede, pero tiene NoData en todas las celdas ráster. Los metadatos ráster constan de bandCount, tamaño de celda, extent, spatialReference, pixelType y noDataValues, entre otras propiedades. Cuando se crea un objeto ráster con el método getRasterInfo(), la propiedad readOnly se define como False de forma predeterminada, de modo que los valores se asignan a este objeto ráster vacío.

outRas = Raster(myRas.getRasterInfo())

También puede crear un objeto de clase rasterInfo vacío y rellenar metadatos ráster con el método FromJSONString() en el objeto rasterInfo(). Posteriormente, este objeto se puede utilizar para crear un ráster vacío, como se muestra a continuación.

ri = arcpy.RasterInfo()
ri_as_jsonstring = '{"bandCount":1,' \
                   '"extent":{"xmin":0,"ymin":0,"xmax":10,"ymax":10,"spatialReference":{"wkid":26912}},' \
                   '"pixelSizeX":1,' \
                   '"pixelSizeY":1,' \
                   '"pixelType":"F32"}'
ri.fromJSONString(ri_as_jsonstring)
outRas = Raster(ri)

Indexar un objeto ráster

Indexar un objeto ráster permite un acceso de lectura y escritura a una celda concreta en un dataset ráster. La indexación se puede utilizar dentro o fuera de un iterador de celda ráster.

En el caso de un ráster de una sola banda, un objeto ráster tiene dos índices, [i, j], los índices de fila y columna, respectivamente. Se requiere un mínimo de dos índices para consultar y asignar valores. Todos los índices se basan en cero y comienzan desde él. Los índices de fila tienen un rango de valores definido por 0 a n(filas) - 1, donde n(filas) es el número de filas de ese ráster. Del mismo modo, los índices de columna tienen un rango de valores definido por 0 a n(columnas) - 1, donde n(columnas) es el número de columnas de ese ráster.

La indexación comienza desde la esquina superior izquierda de un dataset ráster, donde la celda superior situada más a la izquierda presenta un índice de [0, 0], y la celda inferior situada más a la derecha presenta un índice de [n(filas) - 1, n(columnas) - 1].

Manejar celdas NoData

Cuando se consulta una celda con NoData mediante notación de índice, dentro o fuera del contexto de un RCI, el valor que se devuelve en Python es NaN. Por ejemplo, se sabe que myRas tiene un valor NoData para el índice de fila y columna de [2, 2], y la consulta de este valor de celda devolvería NaN. Se puede verificar con el método math.isnan(), que devolvería un valor booleano de True para esta celda.

import math
math.isnan(myRas[2,2])
out: True

Si se asigna una celda NaN en Python, la celda ráster se convierte en NoData, independientemente del formato o NoDataValues de ese dataset ráster. Es la forma correcta de convertir una celda en NoData. Por ejemplo, asignar NaN a myRas en el índice de fila y columna de [3, 3] modificaría esa celda a NoData.

myRas[3,3] = math.nan

Se recomienda que no convierta una celda en NoData asignando los noDataValues de ese dataset ráster directamente a una celda; en cambio, siga el método descrito anteriormente.

Operaciones avanzadas de Iterador de celda ráster

En esta sección, obtendrá una comprensión conceptual de las operaciones avanzadas que puede realizar con RCI. Aprenderá a crear un iterador de celda ráster explícito para iterar sobre varios rásteres y a utilizar las opciones padding y skipNoData. Aprenderá a indexar rásteres multibanda y utilizarlos dentro de un RCI. Aprenderá a administrar distintos tipos de píxel de ráster y a gestionar valores fuera de límites.

Iterador de celda ráster explícito

Otra forma de invocar un RCI es utilizar un objeto de clase RasterCellIterator explícito. Mientras que el iterador implícito es adecuado para iterar sobre un único dataset ráster, el iterador de ráster implícito está diseñado para iterar sobre varios rásteres y se optimiza para este fin. Al iterar sobre varios rásteres, puede realizar un análisis con rásteres de entrada cuyos valores de celda se utilicen en el cómputo, así como rásteres de salida cuyos valores de celda se escriban durante el cómputo.

El ejemplo siguiente muestra el uso de un RCI explícito para iterar sobre varios rásteres de entrada y salida. Los rásteres de entrada myRas1 y myRas2 tienen el mismo número de filas y columnas y otras propiedades como tamaño de celda, extensión y referencia espacial. Los rásteres de salida outRas1 y outRas2 se crean con el objeto de clase rasterInfo del primer ráster de entrada. En cada celda ráster se realiza una operación focal para calcular valores medios para una vecindad de celdas de 3 x 3 para myRas1 y myRas2. En esta ubicación de celda, se asigna a outRas1 un valor de celda como el mínimo de los valores medios y a outRas2 como el máximo de estos valores.

myRas1 = Raster("myras1")
myRas2 = Raster("myras2")
outRas1 = Raster(myRas1.getRasterInfo())
outRas2 = Raster(myRas1.getRasterInfo())
with RasterCellIterator({'rasters':[myRas1, myRas2, outRas1, outRas2]}) as rci:
    for i,j in rci:
        meanMyRas1 = (myRas1[i-1,j-1] + myRas1[i-1, j] + myRas1[i-1, j+1] + \
                      myRas1[i,j-1] + myRas1[i, j] + myRas1[i, j+1] + \
                      myRas1[i+1,j-1] + myRas1[i+1, j] + myRas1[i+1, j+1]) / 9
        meanMyRas2 = (myRas2[i-1,j-1] + myRas2[i-1, j] + myRas2[i-1, j+1] + \
                      myRas2[i,j-1] + myRas2[i, j] + myRas2[i, j+1] + \
                      myRas2[i+1,j-1] + myRas2[i+1, j] + myRas2[i+1, j+1]) / 9
        outRas1[i,j] = min(meanMyRas1, meanMyRas2)
        outRas2[i,j] = max(meanMyRas1, meanMyRas2)
outRas1.save()
outRas2.save()

Cuando se utilizan varios rásteres en el iterador, el primer ráster de la lista define el entorno de análisis de ráster del iterador. Se espera que el resto de rásteres de la lista tengan la misma referencia espacial, tamaño de celda y extensión que el primer ráster. Si estas propiedades del segundo ráster y los siguientes de esta lista no coinciden con las del primer ráster, se les aplica el entorno de análisis del primer ráster. Se recomienda que cree todos los rásteres de salida con el objeto rasterInfo() del primer ráster, para que hereden su referencia espacial, tamaño de celda y extensión.

Además, si desea respetar la configuración del entorno, deben aplicarse al primer ráster de la lista con la función ApplyEnvironment antes de su uso en el iterador. Se demuestra en el ejemplo que aparece a continuación:

myRas1 = Raster("myras1")
myRas2 = Raster("myras2")
#Apply environment settings to myRas1
myRas1_env = arcpy.sa.ApplyEnvironment(myRas1)
outRas1_env = Raster(myRas1_env.getRasterInfo())
with RasterCellIterator({'rasters':[myRas1_env, myRas2, outRas1_env]}) as rci:
    for i,j in rci:
        meanMyRas1 = (myRas1_env[i-1,j-1] + myRas1_env[i-1, j] + myRas1_env[i-1, j+1] + \
                      myRas1_env[i,j-1] + myRas1_env[i, j] + myRas1_env[i, j+1] + \
                      myRas1_env[i+1,j-1] + myRas1_env[i+1, j] + myRas1_env[i+1, j+1]) / 9
        meanMyRas2 = (myRas2[i-1,j-1] + myRas2[i-1, j] + myRas2[i-1, j+1] + \
                      myRas2[i,j-1] + myRas2[i, j] + myRas2[i, j+1] + \
                      myRas2[i+1,j-1] + myRas2[i+1, j] + myRas2[i+1, j+1]) / 9
        outRas1_env[i,j] = min(meanMyRas1, meanMyRas2)
outRas1_env.save()

Opción de relleno

El iterador explícito tiene la opción de ofrecer relleno. El relleno no modifica la salida de ninguna manera, pero mejora el rendimiento si accede a valores de celda de vecindad al iterar por ubicaciones de celda. Por ejemplo, si una operación de vecindad tiene un kernel que accede a valores de celda de hasta dos celdas de fila o dos celdas de columna respecto de la ubicación de la celda con la iteración, proporcione un relleno de 2.

with RasterCellIterator({'rasters':[myRas1, outRas1], 'padding': 2}) as rci_padded:
    for i,j in rci_padded:
        outRas1[i,j] = (myRas1[i-2,j-2] + myRas1[i-2, j] + myRas1[i-2, j+2] + \
                      myRas1[i,j-2] + myRas1[i, j] + myRas1[i, j+2] + \
                      myRas1[i+2,j-2] + myRas1[i+2, j] + myRas1[i+2, j+2]) / 9
outRas1.save()

Opción Omitir NoData

El iterador explícito tiene la capacidad de omitir celdas NoData en su totalidad, en lugar de gestionarlas por celda. Utilice la tecla skipNoData para proporcionar una lista de rásteres que determine las celdas de ráster que omite el iterador. Para cualquier celda determinada, si alguno de los rásteres de esta lista tuviera un valor NoData, esa celda se omitiría. Ya que el estado de celdas NoData en estos rásteres puede cambiar durante el curso de una iteración, el estado de los rásteres en cada celda decide si esta se omite o no cuando el iterador la está visitando. El principal beneficio de esta opción es la mejora del rendimiento al iterar sobre datasets de ráster disperso, ya que no va a ser necesario visitar muchas celdas NoData.

En el siguiente ejemplo, un ráster disperso streamRas tiene valores únicamente en celdas que representan cursos de ríos, y NoData en cualquier otro lugar. Un ráster de dirección de flujo, flowDirRas, tiene valores en todos los lugares dentro de la extensión del ráster sin ninguna celda NoData. Al utilizar RCI para extraer los valores del ráster de dirección de flujo a lo largo de las celdas del curso, puede utilizar skipNoData de tal forma que el iterador solo visite celdas de curso con valores, lo que minimiza el número de celdas totales visitadas.

streamRas = Raster("streams")
flowDirRas = Raster("flowdir")
outRas = Raster(flowDirRas.getRasterInfo())
with RasterCellIterator({'rasters':[streamRas, flowDirRas, outRas],'skipNoData':[flowDirRas, streamRas]}) as rci_skip:
    for i,j in rci_skip:
        outRas[i,j] = flowDirRas[i,j]
outRas.save()

Indexar rásteres multibanda

En el caso de los rásteres multibanda, puede proporcionar dos o tres índices. Las diferencias de comportamiento entre ellos se tratan más adelante.

Si se proporcionan dos índices, se interpretan como de fila y columna y se devuelve una tupla de valores para cada banda del índice [row, column] especificado. Por ejemplo, un ráster multibanda "mbras" con tres bandas se puede consultar con dos índices, como [2,2]. Se interpretan como índices de fila y columna y se devuelven valores de cada una de las tres bandas como una tupla.

multibandRas = Raster("mbras")
multibandRas[2,2]
out: (4,5,6)

Si se especifican tres índices, se interpretan como índices [band, row, column] y el valor de celda se devuelve para esa ubicación de celda de banda, fila y columna. El índice de banda se basa en cero y su rango de valores para un ráster multibanda lo determina 0 a n(bandas) - 1, donde n(bandas) es el número total de bandas.

multibandRas[0,2,2]
out: 4

Manejar valores fuera de límites

Todos los rásteres tienen un tipo de píxel y una profundidad de bit correspondiente. Consulte Capacidad de profundidad de bit para píxeles de dataset ráster para obtener una lista completa de las profundidades de bit y el rango de valores que puede contener cada celda. Cuando se asigna un valor a una celda ráster que supera el rango de valores aceptables para su profundidad de bit, se produce una situación fuera de límites. En tales casos, se asigna NoData a la celda. Por ejemplo, el tipo de píxel de un objeto ráster myRas1 es 'U8', lo que corresponde a un entero de 8 bits sin signo. Este ráster puede contener valores enteros en el rango de 0-255. Si se asigna un valor de 260 a una celda en myRas1, se le asigna NoData. Según esta asignación, consultar el valor en esta celda devolvería NaN.

myRas1 = Raster("myras1") 
myRas1Info = myRas1.getRasterInfo() 
pixelType_MyRas1 = myRas1Info.getPixelType() 
print(pixelType_MyRas1) 
out: 'U8' 
myRas1[3,3] = 260 
math.isnan(myRas1[3,3]) 
out: True

Temas relacionados