Présentation rapide de l’utilisation de l’itérateur de cellule raster

Vous pouvez utiliser le Raster Cell Iterator (RCI, itérateur de cellule raster) pour effectuer une analyse raster personnalisée. Il permet d’accéder aux emplacements des cellules raster individuelles et propose un contrôle d’itération pour interroger et modifier les valeurs des cellules raster, le tout dans un environnement Python.

Opérations de base réalisables avec l’itérateur de cellule raster

Dans cette section, vous allez vous familiariser avec les principes de fonctionnement des opérations de base qu’il est possible de réaliser avec l’itérateur de cellule raster. Vous allez apprendre à créer un itérateur de cellule raster implicite, à interroger des valeurs et en attribuer à des cellules raster, à créer un raster vide avec des métadonnées, à indexer un objet raster et à gérer les cellules NoData. Chaque section comporte des exemples de code qui illustrent les utilisations de l’itérateur de cellule raster dans une fenêtre Python. Il est recommandé de lire entièrement ce document pour bien comprendre ces exemples de code.

Itérateur de cellule raster implicite

Un itérateur de cellule raster peut être appelé implicitement sur un objet raster pour itérer les index de ligne et de colonne d’un raster. Dans l’exemple de code ci-après, un objet raster nommé myRas est créé à partir d’un jeu de données raster existant "myras". Un itérateur de cellule raster implicite est défini sur l’objet raster qui énumère les paires d’index de ligne et de colonne en boucle sur le raster. À l’intérieur de l’itérateur, l’index de ligne i, l’index de colonne j et la valeur de cellule myRas[i,j] sont imprimés à chaque emplacement de cellule. Voici un exemple d’interrogation d’une valeur de cellule à un emplacement de cellule donné à l’aide de la notation d’index.

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

Attribution de valeurs à des cellules

Avec l’itérateur de cellule raster, vous pouvez attribuer des valeurs à des cellules raster et ainsi modifier le jeu de données raster. Par défaut, les objets raster créés à partir d’un jeu de données raster existant comportent une propriété readOnly qui est définie sur True. Pour modifier les jeux de données raster, il convient tout d’abord de définir la propriété readOnly sur False. Une fois que les cellules raster ont été modifiées, la méthode save() doit être appelée sur l’objet raster pour conserver ces modifications. Pour plus d’informations sur la propriété readOnly et l’enregistrement des rasters, consultez la rubrique d’aide consacrée à l’objet Raster.

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

Création de rasters vides

La méthode getRasterInfo() permet de créer facilement un objet raster vide et d’y copier des métadonnées d’un objet raster existant. La méthode getRasterInfo() est appelée sur un objet raster existant, lequel renvoie un objet rasterInfo() pour ce raster. L’objet rasterInfo() est ensuite transmis sous la forme d’un paramètre en entrée à la classe Raster() pour instancier un raster vide. L’objet raster vide contient les mêmes métadonnées que l’objet raster à partir duquel il est créé, mais toutes ses cellules raster ont une valeur NoData. Les métadonnées raster comptent bandCount, extent, spatialReference, pixelType, noDataValues et la taille de cellule, entre autres propriétés. Lorsqu’un objet raster est créé suivant la méthode getRasterInfo(), la propriété readOnly est définie par défaut sur False de façon que les valeurs puissent être attribuées à cet objet raster vide.

outRas = Raster(myRas.getRasterInfo())

Vous pouvez également créer un objet de classe rasterInfo vide et renseigner les métadonnées raster en utilisant la méthode FromJSONString() sur l’objet rasterInfo(). Par la suite, cet objet peut être utilisé pour créer un raster vide comme illustré ci-après.

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)

Indexation d’un objet raster

L’indexation d’un objet raster donne accès en lecture et en écriture à une cellule spécifique d’un jeu de données raster. Il est possible d’utiliser l’indexation à l’intérieur ou à l’extérieur d’un itérateur de cellule raster.

Dans le cas d’un raster monocanal, un objet raster comprend deux index, [i, j], à savoir l’index de ligne et l’index de colonne respectivement. Au moins deux index sont nécessaires pour interroger et attribuer des valeurs. Tous les index sont en base zéro, c’est-à-dire qu’ils commencent à zéro. Les index de ligne présentent une plage de valeurs, définie par 0 à n(rows) - 1, où n(rows) est le nombre de lignes du raster. De la même façon, les index de colonne présentent une plage de valeurs, définie par 0 à n(cols) - 1, où n(cols) est le nombre de colonnes du raster.

L’indexation commence dans l’angle supérieur gauche d’un jeu de données raster, où la cellule tout en haut à gauche a pour index [0, 0], et la cellule tout en bas à droite a pour index [n(rows) - 1, n(cols) - 1].

Gestion des cellules NoData

Lorsqu’une cellule de type NoData est interrogée suivant la notation d’index, par rapport ou non à un itérateur de cellule raster, la valeur renvoyée dans Python est NaN. Par exemple, s’il est avéré que myRas affiche une valeur NoData pour les index de ligne et de colonne [2, 2], l’interrogation de cette valeur de cellule renvoie NaN. Il est possible de le vérifier en appliquant la méthode math.isnan(), qui renvoie la valeur booléenne True pour cette cellule.

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

Si la valeur NaN est attribuée à une cellule dans Python, la cellule raster devient une cellule NoData, indépendamment du format ou de la propriété NoDataValues pour ce jeu de données raster. C’est la méthode à utiliser pour transformer une cellule en cellule NoData. Par exemple, l’attribution de la valeur NaN à myRas pour les index de ligne et de colonne [3, 3] a pour effet de convertir cette cellule en cellule NoData.

myRas[3,3] = math.nan

Il est recommandé de ne pas transformer une cellule en NoData en attribuant la valeur noDataValues de ce jeu de données raster directement à une cellule. Il est préférable de suivre la méthode décrite ci-avant.

Opérations avancées réalisables avec l’itérateur de cellule raster

Dans cette section, vous allez vous familiariser avec les principes de fonctionnement des opérations avancées qu’il est possible de réaliser avec l’itérateur de cellule raster. Vous allez apprendre à créer un itérateur de cellule raster explicite pour procéder à une itération sur plusieurs rasters, et à utiliser les options padding et skipNoData. Vous allez vous initier à l’indexation des rasters multicanaux et y recourir dans un itérateur de cellule raster. Vous allez apprendre à utiliser différents types de pixel de raster et à traiter les valeurs hors limites.

Itérateur de cellule raster explicite

Une autre manière d’appeler un itérateur de cellule raster consiste à utiliser un objet de classe RasterCellIterator explicite. Alors que l’itérateur implicite se prête à l’itération sur un seul jeu de données raster, l’itérateur explicite est destiné à l’itération sur plusieurs rasters et est optimisé à cette fin. Dans le cas d’une itération sur plusieurs rasters, vous pouvez procéder à une analyse avec des rasters en entrée dont les valeurs de cellule sont utilisées dans le calcul et des rasters en sortie dont les valeurs de cellule font l’objet d’une écriture pendant le calcul.

L’exemple suivant présente l’utilisation d’un itérateur de cellule raster explicite pour itérer plusieurs rasters en entrée et en sortie. Les rasters en entrée myRas1 et myRas2 ont le même nombre de lignes et de colonnes, entre autres propriétés telles que la taille de cellule, l’étendue et la référence spatiale. Les rasters en sortie outRas1 et outRas2 sont créés à l’aide de l’objet de classe rasterInfo du premier raster en entrée. À chaque cellule raster, une opération focale est exécutée pour calculer les valeurs moyennes d’un voisinage de 3 cellules par 3 pour myRas1 et myRas2. À cet emplacement de cellule, une valeur de cellule correspondant au minimum des moyennes est attribuée à outRas1, et une valeur de cellule correspondant au maximum des moyennes est attribuée à outRas2.

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()

Lorsque plusieurs rasters sont utilisés dans l’itérateur, le premier raster de la liste définit l’environnement d’analyse raster de l’itérateur. La référence spatiale, la taille de cellule et l’étendue de tous les autres rasters de la liste doivent être identiques à celles du premier raster. Si ces mêmes propriétés du deuxième raster et des suivants de la liste ne correspondent pas à celles du premier raster, l’environnement d’analyse du premier raster leur est appliqué. Il est préconisé de créer tous les rasters en sortie à l’aide de l’objet rasterInfo() du premier raster afin qu’ils héritent de sa référence spatiale, de sa taille de cellule et de son étendue.

Qui plus est, si vous souhaitez conserver les paramètres d’environnement, il convient de les appliquer au premier raster de la liste à l’aide de la fonction ApplyEnvironment avant de l’utiliser dans l’itérateur. C’est ce qui est illustré dans l’exemple ci-après.

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()

Option Padding (Marge de remplissage)

L’itérateur explicite comporte une option qui permet d’indiquer une marge de remplissage. La marge de remplissage ne change en rien la sortie. Toutefois, elle améliore les performances en cas d’accès à des valeurs de cellule de voisinage pendant l’itération dans les emplacements de cellule. Si, par exemple, pendant une opération de voisinage, un noyau accède à des valeurs de cellule situées à une distance maximale de deux cellules de ligne ou deux cellules de colonne de l’emplacement de cellule en cours d’itération, il convient d’indiquer une marge de remplissage 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()

Option Skip NoData (Ignorer NoData)

L’itérateur explicite a la possibilité d’ignorer totalement les cellules NoData, au lieu de les traiter cellule par cellule. Utilisez la clé skipNoData pour fournir une liste des rasters qui permettrait de déterminer quelles cellules raster sont ignorées par l’itérateur. Pour une cellule donnée, si l’un des rasters de cette liste a la valeur NoData, la cellule est ignorée. Dans la mesure où l’état des cellules NoData dans ces rasters peut changer au cours d’une itération, la décision d’ignorer ou non est prise en fonction de l’état des rasters de chaque cellule lors de la visite de l’itérateur. Le principal avantage de cette option est le fait qu’elle améliore les performances lors de l’itération de jeux de données raster creux, dans la mesure où il n’est plus nécessaire de visiter de nombreuses cellules NoData.

Dans l’exemple suivant, un raster creux streamRas a des valeurs uniquement dans les cellules représentant des cours d’eau, et la valeur NoData dans toutes les autres cellules. Un raster de sens de circulation, flowDirRas, a des valeurs partout dans l’étendue du raster, sans aucune cellule NoData. Lorsque vous utilisez le RCI pour extraire les valeurs du raster de sens de circulation le long des cellules de cours d’eau, vous pouvez utiliser la clé skipNoData de façon à ce que l’itérateur ne visite aucune cellule de cours d’eau avec des valeurs, limitant ainsi le nombre total de cellules visitées.

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()

Indexation des rasters multicanaux

Pour les rasters multicanaux, vous pouvez fournir deux ou trois index. Leurs différences de comportement sont présentées ci-après.

Si deux index sont spécifiés, ils sont interprétés comme des index de ligne et de colonne, et un tuple de valeurs est renvoyé par chaque canal pour l’index [row, column] spécifié. Par exemple, un raster multicanal « mbras » de trois canaux peut être interrogé avec deux index, comme [2,2]. Ils sont interprétés comme des index de ligne et de colonne, et les valeurs de chacun des trois canaux sont renvoyées sous la forme d’un tuple.

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

Si trois index sont spécifiés, ils sont interprétés comme des index [band, row, column], et la valeur de cellule est renvoyée pour cet emplacement de cellule de canal, de ligne et de colonne. L’index de canal est en base zéro, et sa plage de valeurs pour un raster multicanal est déterminée par 0 à n(bands) - 1, où n(bands) est le nombre total de canaux.

multibandRas[0,2,2]
out: 4

Gestion des valeurs hors limites

Tous les rasters ont un type de pixel auquel correspond une profondeur de bit. Pour obtenir la liste complète des profondeurs de bit et des plages de valeurs correspondant à chaque cellule, reportez-vous à la rubrique Capacité de profondeur de bit pour les cellules d’un jeu de données raster. Il y a dépassement de limites lorsqu’une valeur est attribuée à une cellule raster qui dépasse la plage de valeurs acceptables pour sa profondeur de bit. Le cas échéant, la valeur NoData est attribuée à la cellule. Prenons l’exemple d’un objet raster myRas1 dont le type de pixel est 'U8', à savoir un entier non signé 8 bits. Ce raster peut contenir des valeurs entières comprises entre 0 et 255. Si la valeur 260 est attribuée à une cellule dans myRas1, cette cellule se voit attribuer la valeur NoData. Dans la logique de cette attribution, l’interrogation de la valeur à cette cellule renvoie 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

Rubriques connexes