Sie können den Raster Cell Iterator (RCI) verwenden, um benutzerdefinierte Raster-Analysen durchzuführen. Mit dem Raster-Zellen-Iterator können einzelne Rasterzellenpositionen erreicht und Iterationen zum Abfragen und Ändern von Rasterzellenwerten gesteuert werden – dies alles in einer Python-Umgebung.
Durchführen grundlegender Vorgänge mit dem Raster-Zellen-Iterator
In diesem Abschnitt erhalten Sie ein konzeptionelles Verständnis der grundlegenden Vorgänge, die mit einem RCI durchgeführt werden können. Sie erfahren, wie Sie einen implizites RCI erstellen, Werte für Rasterzellen abfragen und zuweisen, ein leeres Raster mit Metadaten erstellen, ein Raster-Objekt indizieren und NoData handhaben. Jeder Abschnitt enthält Codebeispiele, die die RCI-Verwendung über ein Python-Fenster veranschaulichen. Es wird empfohlen, dieses Dokument von oben nach unten zu lesen, um diese Codebeispiele besser zu verstehen.
Impliziter Raster-Zellen-Iterator
Ein RCI kann in einem Raster-Objekt implizit aufgerufen werden, um durch die Zeilen- und Spaltenindizes eines Rasters zu iterieren. Im unten angezeigten Codebeispiel wird ein Raster-Objekt mit dem Namen myRas aus einem vorhandenen "myras"-Raster-Dataset erstellt. Im Raster-Objekt ist ein impliziter Raster-Iterator definiert, der Zeilen- und Spaltenindexpaare über das Raster in einer Schleife aufzählt. Innerhalb des Iterators werden Zeilenindex i, Spaltenindex j und Zellenwert myRas[i,j] an jeder Zellenposition ausgegeben. Dies ist ein Beispiel für die Abfrage eines Zellenwertes an einer bestimmten Zellenposition mit Hilfe der Indexschreibweise.
from arcpy.sa import *
myRas = Raster("myras")
for i,j in myRas:
print(i, j, myRas[i,j])
Zuweisen von Werten zu Zellen
Mit einem RCI können Sie Raster-Zellen Werte zuweisen und damit das Raster-Dataset ändern. Standardmäßig weisen Raster-Objekte, die aus einem vorhandenen Raster-Dataset erstellt wurden, eine readOnly-Eigenschaft auf, die auf True festgelegt ist. Für Änderungen von Raster-Datasets sollte die readOnly-Eigenschaft zuerst auf False festgelegt werden. Nachdem die Raster-Zellen geändert wurden, sollte die save()-Methode für das Raster-Objekt aufgerufen werden, damit diese Änderungen erhalten bleiben. Weitere Informationen zur readOnly-Eigenschaft 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 von leeren Rastern
Mit der getRasterInfo()-Methode können Sie ganz einfach ein leeres Raster-Objekt erstellen und Metadaten aus einem vorhandenen Raster-Objekt kopieren. Die getRasterInfo()-Methode wird für ein bestehendes Raster-Objekt aufgerufen, das ein rasterInfo()-Objekt für dieses Raster zurückgibt. Das rasterInfo()-Objekt wird dann als Eingabeparameter an die Raster()-Klasse übergeben, um ein leeres Raster zu instanziieren. Das leere Raster-Objekt enthält die gleichen Metadaten wie das Raster-Objekt, aus dem es erstellt wird, weist jedoch NoData in allen Raster-Zellen auf. Raster-Metadaten bestehen unter anderem aus den Eigenschaften bandCount, Zellengröße, extent, spatialReference, pixelType und noDataValues. Wenn ein Raster-Objekt mit der getRasterInfo()-Methode erstellt wird, wird die readOnly-Eigenschaft standardmäßig auf False festgelegt, 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 mit der FromJSONString()-Methode auf dem rasterInfo()-Objekt ausfüllen. Anschließend kann dieses Objekt verwendet werden, um ein leeres Raster zu erstellen, wie nachfolgend dargestellt.
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)
Indizierung eines Raster-Objektes
Die Indexierung eines Raster-Objektes ermöglicht Lese- und Schreibzugriff auf eine bestimmte Zelle in einem Raster-Dataset. Die Indexierung kann innerhalb oder außerhalb eines Raster-Zellen-Iterators verwendet werden.
Bei einem Einzelband-Raster verfügt ein Raster-Objekt über zwei Indizes – [i, j] –, die die Zeilen- bzw. Spaltenindizes sind. Für die Abfrage und Zuweisung von Werten sind mindestens zwei Indizes erforderlich. Alle Indizes sind nullbasiert und beginnen bei Null. Zeilenindizes haben einen Wertebereich, der durch 0 bis n(rows) - 1 definiert ist, wobei "n(rows)" die Anzahl der Zeilen in diesem Raster ist. Ebenso haben Spaltenindizes einen Wertebereich, der durch 0 bis n(cols) - 1 definiert ist, wobei "n(cols)" die Anzahl der Spalten in diesem Raster ist.
Die Indizierung beginnt in der linken oberen Ecke eines Raster-Datasets, wobei die oberste linke Zelle einen Index von [0, 0] und die unterste rechte Zelle einen Index von [n(rows) - 1, n(cols) - 1] aufweist.
Handhabung von NoData-Zellen
Wenn eine NoData-Zelle mit Hilfe der Indexschreibweise innerhalb oder außerhalb des Kontextes eines RCI abgefragt wird, lautet der in Python zurückgegebene Wert NaN. Wenn beispielsweise bekannt ist, dass myRas einen NoData-Wert für den Zeilenindex und einen Spaltenindex von [2, 2] enthält, wird beim Abfragen dieses Zellenwertes NaN zurückgegeben. Dies kann mit der math.isnan()-Methode überprüft werden, wobei für diese Zelle ein boolescher Wert von True zurückgegeben wird.
import math
math.isnan(myRas[2,2])
out: True
Wenn einer Zelle NaN in Python zugewiesen wird, wird die Raster-Zelle zu NoData, unabhängig vom Format oder von den NoDataValues für dieses Raster-Dataset. Dies ist die richtige Methode, um eine Zelle in NoData umzuwandeln. So wird beispielsweise durch Zuweisen von NaN zu myRas am Zeilen- und Spaltenindex von [3, 3] diese Zelle in NoData geändert.
myRas[3,3] = math.nan
Es wird empfohlen, eine Zelle nicht durch direktes Zuweisen der noDataValues dieses Raster-Datasets zur Zelle in NoData umzuwandeln, sondern die oben beschriebene Methode zu verwenden.
Durchführen erweiterter Vorgänge mit dem Raster-Zellen-Iterator
In diesem Abschnitt erhalten Sie ein konzeptionelles Verständnis für erweiterte Vorgänge, die mit einem RCI durchgeführt werden können. Sie erfahren, wie Sie einen expliziten Raster-Zellen-Iterator erstellen, um über mehrere Raster zu iterieren, und wie Sie die Optionen padding und skipNoData verwenden. Sie lernen die Indizierung von Multiband-Rastern und deren Verwendung in einem RCI. Sie erhalten Informationen dazu, wie Sie verschiedene Raster-Pixeltypen handhaben und wie Sie mit Werten umgehen, die außerhalb des gültigen Bereichs liegen.
Expliziter Raster-Zellen-Iterator
Eine weitere Möglichkeit, ein RCI aufzurufen, ist die Verwendung eines expliziten RasterCellIterator-Klassenobjektes. Während der implizite Iterator für die Iteration über ein einzelnes Raster-Dataset geeignet ist, ist der explizite Raster-Iterator für die Iteration über mehrere Raster ausgelegt und für diesen Zweck optimiert. Während der Iteration über mehrere Raster können Sie eine Analyse mit Eingabe-Rastern durchführen, deren Zellenwerte in den Berechnungs- und Ausgabe-Rastern verwendet werden, die während der Berechnung in diese geschrieben werden.
Das folgende Beispiel zeigt die Verwendung eines expliziten RCI zur Iteration über mehrere Eingabe- und Ausgabe-Raster. Die Eingabe-Raster myRas1 und myRas2 weisen die gleiche Anzahl an Zeilen und Spalten sowie weitere Eigenschaften wie Zellengröße, Ausdehnung und Raumbezug auf. Die Ausgaberaster outRas1 und outRas2 werden mit dem rasterInfo-Klassenobjekt des ersten Eingabe-Rasters erstellt. An jeder Raster-Zelle wird eine fokale Operation durchgeführt, um Mittelwerte für eine aus 3x3 Zellen bestehenden Nachbarschaft (für myRas1 und myRas2) zu berechnen. An dieser Zellenposition wird outRas1 ein Zellenwert als Mindestmittelwert zugewiesen, und outRas2 wird ein Zellenwert als maximaler Mittelwert 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 müssen in Bezug auf Raumbezug, Zellengröße und Ausdehnung mit dem ersten Raster übereinstimmen. Wenn diese Eigenschaften des zweiten Rasters und der nachfolgenden Raster in dieser Liste nicht mit denen des ersten Rasters übereinstimmen, wird die Analyseumgebung des ersten Rasters auf sie angewendet. Es wird empfohlen, alle Ausgabe-Raster mit dem rasterInfo()-Objekt des ersten Rasters zu erstellen, sodass diese die Raumbezug, Zellengröße und Ausdehnung übernehmen.
Wenn Sie darüber hinaus Umgebungseinstellungen berücksichtigen möchten, sollten sie mit der ApplyEnvironment-Funktion auf das erste Raster in dieser Liste angewendet werden, bevor sie im Iterator verwendet werden. Dies wird anhand des folgenden Beispiels veranschaulicht.
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 bietet die Option zur Bereitzustellung eines Abstandshalters. 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 umfasst, der auf Zellenwerte zugreift, die bis zu zwei Zeilenzellen oder zwei Spaltenzellen entfernt von der Zellenposition unter der Iteration liegen, geben Sie einen Abstand von 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()
Option zum Überspringen von NoData-Zellen
Der explizite Iterator ist in der Lage, NoData-Zellen vollständig zu überspringen, sodass nicht jede Zelle für sich verarbeitet werden muss. Verwenden Sie den skipNoData-Schlüssel, um eine Liste mit Rastern anzuzeigen, in der festgelegt wird, welche Rasterzellen der Iterator überspringen soll. Wenn Raster in dieser Liste in einer bestimmten Zelle einen NoData-Wert aufweisen, wird die betreffende Zelle übersprungen. Da sich der Zustand der NoData-Zellen in diesen Rastern im Laufe einer Iteration ändern kann, fällt die Entscheidung darüber, ob die betreffende Zelle übersprungen wird oder nicht, auf der Grundlage des Rasterzustands in jeder Zelle, während der Iterator sie inspiziert. Der Hauptvorteil dieser Option besteht in der Verbesserung der Performance beim Iterieren über Raster-Datasets mit geringer Dichte, da viele NoData-Zellen nicht mehr inspiziert werden müssen.
Im folgenden Beispiel weist das Raster streamRas mit geringer Dichte nur in Zellen mit Flussläufen Werte auf, während alle übrigen Zellen NoData-Zellen sind. Ein Fließrichtungs-Raster, flowDirRas, weist ohne NoData-Zellen in der gesamten Rasterausdehnung Werte auf. Bei Verwendung des RCI zum Extrahieren der Werte aus dem Fließrichtungs-Raster entlang der Wasserlaufzellen können Sie den skipNoData-Schlüssel verwenden, sodass der Iterator lediglich Wasserlaufzellen mit Werten inspiziert, wodurch die Gesamtzahl der inspizierten Zellen auf ein Minimum reduziert 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()
Indexierung von Multiband-Rastern
Für Multiband-Raster können Sie zwei oder drei Indizes angeben. Die Unterschiede in Bezug auf ihr Verhalten werden im Folgenden erläutert.
Wenn zwei Indizes bereitgestellt werden, werden diese als Zeilen- und Spaltenindizes interpretiert, und von jedem Band wird ein Tupel mit Werten für den angegebenen [row, column]-Index zurückgegeben. So kann beispielsweise ein "mbras"-Multiband-Raster mit drei Bändern mit zwei Indizes abgefragt werden, z. B. [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 der Zellenwert wird für diese Band-, Zeilen- und Spaltenzellenposition zurückgegeben. Der Bandindex ist nullbasiert, und sein Wertebereich für ein Multiband-Raster wird durch 0 bis n(bands) - 1 bestimmt, wobei "n(bands)" die Gesamtanzahl der Bänder ist.
multibandRas[0,2,2]
out: 4
Handhabung von außerhalb des gültigen Bereichs liegenden Werte
Alle Raster weist einen Pixeltyp und eine entsprechende Bit-Tiefe auf. Eine vollständige Liste der Bit-Tiefen und des Wertebereichs, die jede Zelle enthalten kann, finden Sie unter Bit-Tiefe für Pixel eines Raster-Datasets. Wenn einer Raster-Zelle ein Wert zugewiesen wird, der außerhalb des Bereichs der zulässigen Werte für ihre Bit-Tiefe befindet, wird der zulässige Bereich überschritten. In solchen Situationen wird der Zelle NoData zugewiesen. Beispiel: Der Pixeltyp eines myRas1-Raster-Objektes ist 'U8', d. h. eine 8-Bit-Ganzzahl ohne Vorzeichen. Dieses Raster kann ganzzahlige Werte im Bereich 0 bis 255 enthalten. Wenn einer Zelle in myRas1 der Wert 260 zugewiesen wird, wird ihr NoData zugewiesen. Nach dieser Zuweisung gibt die Abfrage des Wertes an dieser Zelle NaN zurück.
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