Breve introducción al uso del Iterador de celda ráster
Puede utilizar el Raster Cell Iterator (RCI) para realizar análisis ráster personalizados. Permite visitar ubicaciones concretas de celdas de ráster y ofrece control de iteración para consultar y modificar los valores de las celdas de ráster, todo ello en un entorno de Python.
Operaciones básicas con el Iterador de celdas ráster
En esta sección, adquirirá una comprensión conceptual de las operaciones básicas que se pueden realizar mediante RCI. Aprenderá a crear un RCI implícito, consultar y asignar valores a las celdas de una ráster, crear un ráster vacío con metadatos, indexar un objeto ráster y administrar los valores NoData. Cada sección incluye ejemplos de código que muestran cómo utilizar RCI desde una ventana de Python. Se recomienda leer este documento en su totalidad para comprender estos ejemplos de código.
Iterador implícito de celdas ráster
Un RCI se puede invocar de forma implícita en un objeto ráster para iterar los índices de fila y columna de dicho ráster. El ejemplo de código que se muestra a continuación crea un objeto ráster denominado myRas a partir de un dataset ráster "myras" existente. En el objeto ráster se define un iterador implícito que recorre, en un bucle, los pares de índices de fila y columna a lo largo del ráster. Dentro del iterador, se muestran el índice de fila i, el índice de columna j y el valor de la celda myRas[i,j] en cada ubicación de celda. Este es un ejemplo de cómo consultar el valor de una celda en una ubicación determinada utilizando la 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
Con RCI, puede asignar valores a las celdas ráster y, de este modo, modificar el dataset ráster. De forma predeterminada, los objetos ráster creados a partir de un dataset ráster existente tienen una propiedad readOnly está establecida en True. Para modificar datasets ráster, primero es preciso establecer la propiedad readOnly en False. Una vez modificadas las celdas ráster, se debe invocar el método save() en el objeto ráster para que los cambios se guarden. Para obtener más información sobre la propiedad readOnly y cómo guardar rásteres, consulte el tema de ayuda sobre el 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
Este método getRasterInfo() permite crear fácilmente un objeto ráster vacío y copiar en él los metadatos de un objeto ráster existente. El método getRasterInfo() se invoca sobre un objeto ráster existente, lo que devuelve un objeto rasterInfo() para dicho ráster. A continuación, el objeto rasterInfo() se pasa como parámetro de entrada a la clase Raster() para crear una instancia de un ráster vacío. El objeto ráster vacío tiene los mismos metadatos que el objeto ráster a partir del cual se ha creado, pero todas las celdas ráster contienen el valor NoData. Los metadatos ráster constan de bandCount, el tamaño de la celda, extent, spatialReference, pixelType y noDataValues, entre otras propiedades. Cuando se crea un objeto ráster mediante el método getRasterInfo(), la propiedad readOnly se establece en False de forma predeterminada, de modo que se puedan asignar valores a este objeto ráster vacío.
outRas = Raster(myRas.getRasterInfo())
También puede crear un objeto vacío de la clase rasterInfo y rellenar los metadatos ráster utilizando el método FromJSONString() del objeto rasterInfo(). Posteriormente, este objeto se puede utilizar para crear un ráster vacío, tal y 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 raster
Indexar un objeto ráster permite tener acceso de lectura y escritura a una cela específica de un dataset ráster. La indexación se puede utilizar tanto dentro como fuera de un iterador de celdas ráster.
En el caso de un ráster de una sola banda, un objeto ráster tiene dos índices, [i, j], que son los índices de fila y columna, respectivamente. Se requieren al menos dos índices para realizar consultas y asignar valores. Todos los índices están basados en cero, es decir, empiezan por cero. Los índices de fila tienen un rango de valores que va de 0 a n(filas) - 1, donde n(filas) es el número de filas del ráster en cuestión. Del mismo modo, los índices de columna tienen un rango de valores definido entre 0 y n(columnas) - 1, donde n(columnas) es el número de columnas del ráster en cuestión.
La indexación comienza en la esquina superior izquierda de un dataset ráster, donde la celda situada en el extremo superior izquierdo tiene un índice de [0, 0], y la celda situada en el extremo inferior derecho tiene un índice de [n(filas) - 1, n(columnas) - 1].
Manejo de celdas NoData
Cuando se consulta una celda con el valor NoData mediante notación de índice, ya sea dentro o fuera del contexto de un RCI, el valor que devuelve Python es NaN. Por ejemplo, si se sabe que myRas tiene un valor NoData para la fila y un índice de columna de [2, 2], al consultar el valor de esta celda se obtendría NaN. Esto se puede comprobar utilizando 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 NaN a una celda en Python, la celda de la ráster pasa a ser NoData, independientemente del formato o NoDataValues de ese dataset ráster. Esta es la forma correcta de establecer una celda como NoData. Por ejemplo, asignar NaN a myRas en el índice de fila y columna [3, 3] modificará la celda a NoData.
myRas[3,3] = math.nan
Se recomienda no establecer el valor de una celda en NoData asignando noDataValues del dataset ráster directamente a una celda y, en cambio, seguir el método descrito anteriormente.
Operaciones avanzadas con el Iterador de celdas ráster
En esta sección, adquirirá una comprensión conceptual de las operaciones avanzadas que se pueden realizar mediante RCI. Aprenderá a crear un iterador explícito de celdas ráster para iterar sobre varios rásteres, y a utilizar las opciones padding y skipNoData. Aprenderá a indexar rásteres multibanda y a utilizarlos en un RCI. Aprenderá a manejar los diferentes tipos de píxeles ráster y a tratar los valores fuera de rango.
Iterador explícito de celdas ráster
Otra forma de invocar un RCI es utilizando un objeto de clase RasterCellIterator explícito. Mientras que el iterador implícito es adecuado para iterar un único dataset ráster, el iterador ráster explícito está diseñado para iterar sobre varios rásteres y está optimizado para este fin. Al iterar sobre varios rásteres, es posible realizar análisis utilizando rásteres de entrada, cuyos valores de celda se usan en los cálculos, y rásteres de salida, cuyos valores de celda se escriben a medida que se ejecuta el procesamiento.
El siguiente ejemplo muestra cómo utilizar 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, así como otras propiedades, como el tamaño de celda, la extensión y la referencia espacial. Los rásteres de salida outRas1 y outRas2 se crean utilizando la clase de objeto rasterInfo del primer ráster de entrada. En cada celda ráster, se lleva a cabo una operación focal para calcular los valores medios de una vecindad de 3 por 3 celdas para myRas1 y myRas2. En la ubicación de esta celda, se asigna a outRas1 un valor de celda que corresponde al mínimo de las medias, y a outRas2 se le asigna un valor de celda que corresponde al máximo de las medias.
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 ráster del iterador. Se espera que el resto de rásteres de la lista coincidan en referencia espacial, tamaño de celda y extensión con los del primer ráster. Si estas propiedades del segundo y siguientes rásteres 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 crear todos los rásteres de salida utilizando el objeto rasterInfo() del primer ráster, de modo 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 esta lista mediante la función ApplyEnvironment antes de utilizarlo en el iterador. Esto se ilustra en el ejemplo que figura 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 una opción para proporcionar 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 núcleo que accede a valores de celdas situadas a una distancia de hasta dos celdas en fila o dos celdas en columna respecto a la celda objeto de la iteración, se debe especificar 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()
Omitir la opción NoData
El iterador explícito tiene la capacidad de omitir por completo las celdas NoData, en lugar de tratarlas una por una. Utilice la clave skipNoData para proporcionar una lista de rásteres que determinará qué celdas de ráster debe omitir el iterador. Para cualquier celda, si alguna de las imágenes ráster de esta lista tiene un valor NoData, dicha celda se omitirá. Dado que el estado de las celdas NoData en estos rásteres puede cambiar a lo largo de una iteración, la decisión de aplicar o no la omisión viene determinada por el estado de los rásteres en cada celda, a medida que el iterador los visita. La principal ventaja de esta opción es la mejora del rendimiento al iterar sobre datasets ráster dispersos, ya que ya no es necesario visitar muchas celdas NoData.
En el siguiente ejemplo, un ráster disperso streamRas solo tiene valores en las celdas que representan cursos fluviales, y NoData en el resto. Un ráster de dirección del flujo, flowDirRas, tiene valores en todos los puntos dentro de la extensión del ráster, sin celdas NoData. Al utilizar un RCI para extraer los valores ráster de la dirección del flujo a lo largo de las celdas del curso de agua, puede utilizar la clave skipNoData de tal forma que el iterador solo recorra las celdas del curso de agua que contengan valores, minimizando así el número total de celdas 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()
Indexación de rásteres multibanda
En el caso los rásteres multibanda, puede especificar dos o tres índices. A continuación se analizan las diferencias de comportamiento entre ambos.
Si se proporcionan dos índices, estos se interpretan como índices de fila y columna, y se devuelve una tupla de valores de cada banda para el índice [row, column] especificado. Por ejemplo, se puede realizar una consulta en un ráster multibanda "mbras" con tres bandas utilizando dos índices, por ejemplo, [2,2]. Estos se interpretan como índices de fila y columna, y los valores de cada una de las tres bandas se devuelven en forma de tupla.
multibandRas = Raster("mbras")
multibandRas[2,2]
out: (4,5,6)
Si se especifican tres índices, estos se interpretan como índices [band, row, column], y se devuelve el valor de la celda correspondiente a esa banda, fila y columna. El índice de banda se basa en cero y su rango de valores para ráster multibanda va de 0 a n(bandas) - 1, donde n(bandas) es el número total de bandas.
multibandRas[0,2,2]
out: 4
Manejo de valores fuera de límite
Todos los rásteres tienen un tipo de píxel y la profundidad de bits correspondiente. Consulte Capacidad de profundidad de bits para los píxeles de los datasets ráster para obtener una lista completa de las profundidades de bits y del rango de valores que puede contener cada celda. Cuando se asigna a una celda ráster un valor que supera el rango de valores permitido para su profundidad de bits, se produce una situación de desbordamiento. En tales situaciones, a la celda se le asigna NoData. Por ejemplo, el tipo de píxel de un objeto ráster myRas1 es 'U8', que es un entero de 8 bits sin signo. Este ráster puede contener valores enteros comprendidos entre 0 y 255. Si se asigna el valor 260 a una celda en myRas1, se le asigna NoData. Tras esta asignación, al consultar el valor de esta celda se obtendrí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