Skip to main content

Kurzer Überblick über die Verwendung des Raster-Zellen-Iterators

Mit dem Raster Cell Iterator (RCI) können Sie benutzerdefinierte Raster-Analysen durchführen. Er ermöglicht das Durchlaufen einzelner Raster-Zellenpositionen und bietet eine Iterationskontrolle, sodass Sie Zellenwerte in einem Raster in einer Python-Umgebung abfragen und verändern können.

Grundlegende Funktionen der Verwendung des Raster-Zellen-Iterators

In diesem Abschnitt erhalten Sie ein konzeptionelles Verständnis der grundlegenden Funktionen des RCI. Sie erfahren hier Folgendes: Erstellen eines impliziten RCI, Abfrage von Raster-Zellen und Zuweisen von Werten zu Raster-Zellen, Erstellen eines leeren Rasters mit Metadaten, Indizieren eines Raster-Objekts und Umgang mit NoData. Jeder Abschnitt enthält Codebeispiele, die zeigen, wie man RCI über ein Python-Fenster verwendet. Es wird empfohlen, dieses Dokument der Reihe nach zu lesen, um die Codebeispiele zu verstehen.

Impliziter Raster-Zellen-Iterator

Ein RCI kann implizit in einem Raster-Objekt aufgerufen werden, um alle Zeilen- und Spaltenindizes eines Rasters zu durchlaufen. Das nachstehende Codebeispiel erzeugt ein Raster-Objekt "myras", das aus einem vorhandenen Raster-Dataset myRas aufgerufen wird. Ein impliziter Raster-Iterator ist im Raster-Objekt definiert, das Zeilen- und Spaltenindexpaare über das Raster in einer Schleife durchläuft. Innerhalb des Iterators werden an jeder Zellenposition der Zeilenindex i, der Spaltenindex j und der Zellenwert myRas[i,j] ausgegeben. Dies ist ein Beispiel für die Abfrage eines Zellenwerts an einer bestimmten Zellenposition mithilfe der Indexschreibweise.

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

Zuweisen von Werten für Zellen

Mit RCI können Sie Raster-Zellenwerte zuweisen und so das Raster-Dataset verändern. Standardmäßig haben Raster-Objekte, die aus einem vorhandenen Raster-Dataset erstellt werden, die Eigenschaft readOnly, die auf True gesetzt ist. Um Raster-Datensätze zu modifizieren, sollte die Eigenschaft readOnly zunächst auf False gesetzt werden. Sobald die Raster-Zellen verändert wurden, sollte die Methode save() auf dem Raster-Objekt aufgerufen werden, damit diese Änderungen erhalten bleiben. Weitere Informationen zur Eigenschaft readOnly und zum Speichern von Rastern finden Sie im Hilfethema zu Raster-Objekten.

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

Erstellen leerer Raster

Mit der getRasterInfo()-Methode können Sie einfach ein leeres Raster-Objekt erstellen und Metadaten aus einem vorhandenen Raster-Objekt kopieren. Die Methode getRasterInfo() wird in einem vorhandenen Raster-Objekt aufgerufen, das ein rasterInfo()-Objekt für dieses Raster zurückgibt. Das rasterInfo()-Objekt wird dann als Eingabeparameter an die Raster()-Klasse übergegeben, um ein leeres Raster zu instanziieren. Das leere Raster-Objekt weist dieselben Metadaten auf wie das Raster-Objekt, aus dem es erstellt wurde, enthält jedoch in allen Raster-Zellen NoData. Raster-Metadaten bestehen aus bandCount, Zellgröße, extent, spatialReference``pixelType und noDataValues sowie weiteren Eigenschaften. Wenn ein Raster-Objekt mit der getRasterInfo()-Methode erstellt wird, wird die Eigenschaft readOnly standardmäßig auf False gesetzt, sodass diesem leeren Raster-Objekt Werte zugewiesen werden können.

outRas = Raster(myRas.getRasterInfo())

Sie können auch ein leeres rasterInfo-Klassenobjekt erstellen und Raster-Metadaten füllen, indem Sie die FromJSONString()-Methode für das rasterInfo()-Objekt ausführen. Anschließend kann das Objekt zur Erstellung eines leeren Rasters verwendet werden.

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)

Indizieren eines Raster-Objekts

Das Indizieren eines Raster-Objekts ermöglicht den Lese- und Schreibzugriff auf eine bestimmte Zelle in einem Raster-Dataset. Die Indizierung kann innerhalb oder außerhalb eines Raster-Zell-Iterators verwendet werden.

Für ein Einband-Raster hat ein Raster-Objekt zwei Indizes: [i, j]. Dies sind Zeilen- bzw. Spaltenindizes. Es sind mindestens zwei Indizes erforderlich, um Werte abzufragen und zuzuweisen. Alle Indizes sind nullbasiert und beginnen bei null. Zeilenindizes haben einen Wertebereich, der von 0 bis n(Zeilen) – 1 definiert ist, wobei n(Zeilen) die Anzahl der Zeilen in diesem Raster ist. Ähnlich haben Spaltenindizes einen Wertebereich, der von 0 bis n(Spalten) – 1 definiert ist, wobei n(Spalten) die Anzahl der Spalten in diesem Raster ist.

Die Indizierung beginnt in der oberen linken Ecke eines Raster-Datasets, wobei die obere linke Zelle einen Index von [0, 0] und die untere rechte Zelle einen Index von [n(Zeilen) – 1, n (Spalten) – 1] hat.

Umgang mit NoData-Zellen

Wenn eine Zelle mit NoData mittels Indexschreibweise innerhalb oder außerhalb des Kontexts eines RCI abgefragt wird, wird in Python der Wert NaN zurückgegeben. Zum Beispiel, wenn bekannt ist, dass myRas einen NoData-Wert für die Zeile und den Spaltenindex [2, 2] aufweist, würde die Abfrage dieses Zellenwerts den Wert NaN zurückgeben. Das kann mit der math.isnan()-Methode verifiziert werden, die für diese Zelle den Booleschen Wert True zurückgeben würde.

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

Wenn eine Zelle in Python zu NaN zugewiesen wird, wird die Raster-Zelle zu NoData, unabhängig vom Format oder NoDataValues für dieses Raster-Dataset. Das ist die richtige Methode, um eine Zelle in NoData umzuwandeln. Zum Beispiel würde sich bei Zuweisung von NaN zu myRas am Zeilen- und Spaltenindex [3, 3] diese Zelle in NoData ändern.

myRas[3,3] = math.nan

Es wird empfohlen, eine Zelle nicht direkt in NoData zu überführen, indem Sie die noDataValues-Zelle dieses Raster-Datasets einer Zelle zuweisen, sondern stattdessen der oben beschriebenen Methode zu folgen.

Erweiterte Operationen mit Raster-Zell-Iterator

In diesem Abschnitt erhalten Sie ein konzeptionelles Verständnis der fortgeschrittenen Funktionen des RCI. Sie erfahren, wie Sie einen expliziten Raster-Zellen-Iterator erstellen, um mehrere Raster zu durchlaufen, und die Optionen padding und skipNoData verwenden. Sie lernen das Indizieren von Multiband-Rastern und ihre Verwendung innerhalb eines RCI kennen. Sie erfahren, wie man mit verschiedenen Raster-Pixeltypen und mit außerhalb des Bereichs liegenden Werten umgeht.

Expliziter Raster-Zellen-Iterator

Eine weitere Möglichkeit, einen RCI zu aktivieren, ist die Verwendung eines expliziten RasterCellIterator-Klassenobjekts. Während der implizite Iterator zum Durchlaufen eines einzelnen Raster-Datasets geeignet ist, wurde der explizite Raster-Iterator dafür optimiert, mehrere Raster zu durchlaufen. Beim Iterieren mehrerer Raster können Sie Analysen mit Eingabe-Rastern durchführen, deren Zellwerte in die Berechnung einfließen, und bei denen Ausgaberaster verwendet werden, in die die Zellenwerte während der Berechnung geschrieben werden.

Das folgende Beispiel zeigt, wie mithilfe eines expliziten RCI mehrere Ein- und Ausgabe-Raster durchlaufen werden. Die Eingabe-Raster myRas1 und myRas2 weisen dieselbe Anzahl an Zeilen und Spalten sowie weitere gemeinsame Eigenschaften wie Zellgröße, Ausdehnung und räumliche Referenz auf. Die Ausgabe-Raster outRas1 und outRas2 werden mit dem rasterInfo-Klassenobjekt des ersten Eingabe-Rasters erstellt. An jeder Raster-Zelle wird eine Fokaloperation durchgeführt, um Mittelwerte für eine Zellumgebung der Größe 3 mal 3 für myRas1 und myRas2 zu berechnen. An dieser Zellenposition wird ein Zellenwert als Minimum der Mittelwerte zu outRas1 und ein Zellwert als Maximum der Mittelwerte zu outRas2 zugewiesen.

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

Wenn mehrere Raster im Iterator verwendet werden, definiert das erste Raster in der Liste die Raster-Analyseumgebung des Iterators. Alle anderen Raster in der Liste sollen in Raumbezug, Zellgröße und Ausdehnung mit dem ersten Raster übereinstimmen. Wenn diese Eigenschaften des zweiten und der nachfolgenden Raster in dieser Liste nicht mit denen des ersten übereinstimmen, wird die Analyseumgebung des ersten Rasters auf die nachfolgenden Raster angewendet. Es wird empfohlen, alle Ausgabe-Raster mit dem rasterInfo()-Objekt des ersten Rasters zu erstellen, sodass sie dessen Raumbezug, Zellengröße und Ausdehnung übernehmen.

Wenn Sie zudem die Umgebungseinstellungen berücksichtigen möchten, sollten diese mithilfe der ApplyEnvironment-Funktion auf das erste Raster in dieser Liste angewendet werden, bevor Sie es im Iterator verwenden. Dies wird im nachstehenden Beispiel demonstriert.

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

Abstandshalter-Option

Der explizite Iterator verfügt über eine Option zum Abstandhalten. Ein Abstandshalter verändert die Ausgabe nicht, verbessert jedoch die Performance, wenn Sie während der Iteration durch Zellenpositionen auf Werte benachbarter Zellen zugreifen. Wenn beispielsweise eine Nachbarschaftsoperation einen Kernel hat, der auf Zellenwerte bis zu zwei Zeilen- oder zwei Spaltenzellen außerhalb der Zellenposition zugreift, geben Sie den Abstand 2 an.

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

Überspringen der NoData-Option

Der explizite Iterator kann NoData-Zellen komplett überspringen, anstatt sie pro Zelle zu verarbeiten. Verwenden Sie den skipNoData-Schlüssel, um eine Liste von Rastern bereitzustellen, die ermittelt, welche Raster-Zellen vom Iterator übersprungen werden. Wenn eine beliebige Zelle eines der Raster in dieser Liste einen NoData-Wert aufweist, würde diese Zelle übersprungen. Da sich der Zustand der NoData-Zellen in diesen Rastern im Verlauf einer Iteration ändern kann, wird das Überspringen durch den Zustand der Raster in jeder Zelle bestimmt, während sie vom Iterator durchlaufen werden. Der Hauptvorteil dieser Option liegt in einer Steigerung der Iterations-Performance bei Datasets mit Rastern geringer Dichte, da viele NoData-Zellen nicht mehr durchlaufen werden müssen.

Im folgenden Beispiel weist ein Raster streamRas mit geringer Dichte nur Werte in Zellen auf, die Flussläufe repräsentieren, und ansonsten Werte ohne NoData-Zellen. Ein Fließrichtungs-Raster flowDirRas weist überall innerhalb der Raster-Ausdehnung Werte ohne NoData-Zellen auf. Wenn Sie RCI verwenden, um die Raster-Werte für die Fließrichtung entlang der Flusszellen zu extrahieren, kann der skipNoData-Schlüssel so verwendet werden, dass der Iterator nur Flusszellen mit Werten durchläuft, wodurch die Anzahl der durchlaufenen Zellen minimiert wird.

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

Indizierung von Multiband-Rastern

Für Multiband-Raster können Sie zwei oder drei Indizes angeben. Ihre unterschiedlichen Verhaltensweisen werden im Folgenden erläutert.

Wenn zwei Indizes bereitgestellt werden, werden diese als Zeilen- und Spaltenindizes interpretiert, und von jedem Band wird für den angegebenen [row, column]-Index ein Tupel von Werten zurückgegeben. Zum Beispiel kann ein Multiband-Raster "mbras" mit drei Bändern mit zwei Indizes abgefragt werden, zum Beispiel [2,2]. Diese werden als Zeilen- und Spaltenindizes interpretiert, und Werte aus jedem der drei Bänder werden als Tupel zurückgegeben.

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

Wenn drei Indizes angegeben sind, werden diese als [band, row, column]-Indizes interpretiert, und es wird der Zellenwert für diese Band-, Zeilen- und Spaltenposition zurückgegeben. Der Bandindex ist nullbasiert und sein Wertebereich für ein Multiband-Raster wird durch 0 bis n(Bänder) – 1 bestimmt, wobei n(Bänder) die Gesamtzahl der Bänder darstellt.

multibandRas[0,2,2]
out: 4

Handhabung von Werten, die außerhalb des Bereichs liegen

Alle Raster haben einen Pixeltyp und eine entsprechende Bittiefe. Eine vollständige Liste der Bittiefen und des Wertebereichs, die jede Zelle enthalten kann, finden Sie unter Bittiefe für Raster-Dataset-Pixel. Wenn einer Raster-Zelle ein Wert zugewiesen wird, der den zulässigen Wertebereich für die Bittiefe überschreitet, liegt eine Out-of-bound-Situation vor. In solchen Situationen wird der Zelle "NoData" zugewiesen. Beispiel: Der Pixeltyp eines Raster-Objekts myRas1 lautet 'U8', eine 8-Bit-Ganzzahl ohne Vorzeichen. Dieses Raster kann ganzzahlige Werte im Bereich von 0 bis 255 enthalten. Wenn einer Zelle in myRas1 der Wert 260 zugewiesen wird, wird sie zu einer NoData-Zelle. Nach dieser Zuweisung würde bei einer Abfrage des Wertes dieser Zelle NaN zurückgegeben.

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