Краткий обзор использования Итератора ячеек растра

Используйте Raster Cell Iterator (RCI) для выполнения пользовательского анализа растров. Это даст вам возможность анализировать местоположения отдельных ячеек растра и контролировать итерации при запросе и модификации ячеек растра, в среде Python.

Базовые операции, использующие Итератор ячеек растра

В этом разделе вы получите базовые представления об основных операциях, которые могут быть выполнены с помощью RCI. Вы узнаете: как создать неявный RCI, запрашивать и назначать значения ячейкам растра, как создать пустые растры с метаданным, как индексируются растровые объекты и поддерживаются значения NoData. Каждый раздел содержит примеры кода, демонстрирующие использование RCI в окне Python. Рекомендуется внимательно изучить это статью, чтобы понять принцип написания кодов.

Неявный итератор ячеек растра

RCI может быть неявно применен к растровому объекту для итерации по индексам строк и столбцов растра. Ниже приведен пример кода, в котором создается растровый объект myRas из существующего набора растровых данных "myras". Неявный итератор растра задается для растрового объекта и перечисляет пары индексов строк и столбцов по всему растру в рамках циклической операции. В процессе итерации для каждого местоположения ячейки печатаются индекс строки i, индекс столбца j, и значение ячейки myRas[i,j]. Ниже приведен пример кода для запроса значения ячейки в указанном местоположении ячейки с использованием указания индекса.

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

Назначение значений ячейкам

С использованием RCI вы можете назначать значения ячейкам растра, таким образом модифицируя набор растровых данных. По умолчанию растровые объекты, созданные из существующего набора растровых данных включают свойство readOnly, установленное на True. Для изменения наборов растровых данных, свойство readOnly сначала надо установить на False. После изменений ячеек растра необходимо вызвать метод save() для растрового объекта, для закрепления примененных изменений. Дополнительную информацию о свойстве readOnly и сохранении растров см. в разделе справки Объект Raster.

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

Создание пустых растров

Метод getRasterInfo() позволяет вам просто создать пустой растровый объект и скопировать в него метаданные из существующего растрового объекта. Метод getRasterInfo() вызывается для существующего растрового объекта, и возвращает объект rasterInfo() для этого растра. Объект rasterInfo() затем передается как входной параметр в класс Raster() в виде образца для пустого растра. Пустой растровый объект содержит те же метаданные, как и объект, из которого он сделан, но для всех его ячеек определено значение NoData. Метаданные растра включают bandCount, размер ячейки, extent, spatialReference, pixelType и noDataValues, а также другие свойства. Если растровый объект был создан с помощью метода getRasterInfo(), свойство readOnly установлено на False по умолчанию, поэтому такие значения могут быть присвоены пустому растровому объекту.

outRas = Raster(myRas.getRasterInfo())

Вы также можете создать пустой объектный класс rasterInfo и заполнить метаданные растра с помощью метода FromJSONString() для объекта rasterInfo(). Впоследствии этот объект можно использовать для создания пустого растра, как показано ниже.

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)

Индексация растрового объекта

Индексация растрового объекта дает возможность считывать и вносить значения конкретным ячейкам набора растровых данных. Индексацию можно использовать как в пределах, так и за пределами Итератора ячеек растра.

Одноканальный растр включает два индекса, [i, j], то есть, индексация выполняется по строкам и по столбцам. Для запроса и назначения значений необходимо указать два индекса. Нумерация индексов начинается с нуля. Индексы строк представлены диапазоном значений, от 0 до n (строк) - 1, где n (строк) – число строк в растре. Соответственно, индексы столбцов представлены диапазоном значений, от 0 до n (столбцов) - 1, где n (столбцов) – число столбцов в растре.

Индексация начинается от верхнего левого угла набора растровых данных, где индекс верхней левой ячейки – [0, 0], в свою очередь, индекс нижней правой ячейки – [n(rows) - 1, n(cols) - 1].

Поддержка ячеек NoData

При запросе ячейки со значением NoData с использованием указания индекса, в рамках RCI, или вне инструмента, Python возвращает значение NaN. Например, если известно, что myRas содержит значение NoData для строки и столбца с индексом [2, 2], запрос значения этой ячейки возвратит NaN. Это можно проверить с использованием метода math.isnan(), который возвращает Булево значение True для этой ячейки.

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

Если ячейке присвоено значение NaN в Python, ячейка растра будет преобразована в NoData, независимо от формата или NoDataValues для этого набора растровых данных. Это удобный способ изменить значение ячейки на NoData. Например, назначение NaN для myRas в строке и столбце с индексом [3, 3] изменит значение ячейки на NoData.

myRas[3,3] = math.nan

Рекомендуется не менять значения ячеек на NoData путем присвоения noDataValues набора растровых данных напрямую ячейке; лучше использовать метод, описанный выше.

Дополнительные операции, использующие Итератор ячеек растра

В этом разделе вы получите базовые представления об дополнительных операциях, которые могут быть выполнены с помощью RCI. Вы узнаете, как создать явный Итератор ячеек растра для итерации нескольких растров, а также, как использовать опции padding и skipNoData. Вы узнаете, как выполняется индексация многоканальных растров и как они используются в RCI. Вы узнаете, каким образом обрабатываются различные типы пикселов растров, и как работать с несвязанными значениями.

Явный итератор ячеек растра

Другой способом вызова RCI является использование явного объектного класса RasterCellIterator. Неявный итератор подходит для итерации отдельного набора растровых данных, а явный итератор растра используется для итерации по нескольким растрам и оптимизирован для этой цели. В процессе итерации по нескольким растрам вы можете выполнять анализ на входных растров, ячейки которых используются в вычислении, и выходных растрах, значения ячеек которых записываются в процессе вычислений.

Следующий пример демонстрирует использование явного RCI для итерации по нескольким входным и выходным растрам. Входные растры myRas1 и myRas2 содержат одинаковое число строк и столбцов, в также другие свойства, такие как размер ячеек, экстент и пространственная привязка. Выходные растры outRas1 и outRas2 создаются с использованием объектного класса rasterInfo первого входного растра. Для каждой ячейки растра выполняется фокальная функция для вычисления среднего значения окрестности 3 на 3 ячейки для myRas1 и myRas2. В местоположении этой ячейки, растру outRas1 назначается минимальное среднее значение, а 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()

Когда в итераторе используется несколько растров, первый растр в списке определяет среду анализа растров итератора. Ожидается, что все другие растры в списке будут совпадать по пространственной привязке, размеру ячейки и экстенту с таковой у первого растра. Если эти свойства второго и последующих растров не соответствуют первому растру, среда анализа первого растра применяется ко всем остальным. Рекомендуется создать все выходные растры, используя объект rasterInfo() первого растра, чтобы они наследовали его пространственную привязку, размер ячейки и экстент

Дополнительно, если вы хотите поддерживать настройки параметров среды, их необходимо применить к первому растру в списке с применением функции ApplyEnvironment перед использованием итератора. Эта демонстрируется в примере ниже.

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

Опция отступа

Явный итератор включает опцию, разрешающую внутренний отступ. Внутренний отступ не меняет выходные данные, но увеличивает производительность, если вы обращаетесь к значениям соседних ячеек в процессе итерации местоположений ячеек. Например, если операция использует ядро окрестности, с использованием до 2х ячеек строк или столбцов от итерируемой ячейки, укажите значение внутреннего отступа 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()

Опция Пропустить NoData

Явный итератор имеет возможность полностью пропустить ячейки NoData, вместо того чтобы обрабатывать их для каждой ячейки. Используйте ключ skipNoData, чтобы предоставить список растров, которые будут определять, какие ячейки растра пропускаются итератором. Для любой заданной ячейки, если какой-либо из растров в этом списке имеет значение NoData, эта ячейка будет пропущена. Поскольку состояние ячеек NoData в этих растрах может изменяться в течение итерации, применение пропуска определяется состоянием растров в каждой ячейке, по мере прохождения её итератором. Основное преимущество этого варианта заключается в повышении производительности при итерации по разреженным наборам растровых данных, поскольку многие ячейки NoData больше не нуждаются в прохождении.

В следующем примере разреженный растр streamRas имеет значения только в ячейках, представляющих речные потоки, а в других местах содержит значения NoData. Растр направления потока, flowDirRas, имеет значения везде в пределах экстента растра без ячеек NoData. При использовании RCI для извлечения значений растра направления потока вдоль ячеек потока можно использовать ключ skipNoData таким образом, чтобы итератор посещал только ячейки потока со значениями, минимизируя общее количество посещенных ячеек.

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

Индексация многоканальных растров

Для многоканальных растров вы можете использовать от одного до трех индексов. Различия в поведении описаны ниже.

Если заданы два индекса, они интерпретируются как индексы строк и столбцов, и для каждого канала, при указании индекса [row, column] возвращается кортеж значений. Например, если к многоканальному растру "mbras", содержащему три канала, нужно построить запрос с двумя индексами, наберите [2,2]. Они интерпретируются как индексы строк и столбцов, и значения для каждого из трех каналов возвращаются в виде кортежа.

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

Если указываются три индекса, они интерпретируются как индексы [band, row, column], и для этого местоположение возвращается значение ячейки соответствующего канала, строки и столбца. Индексы каналов начинаются от нуля и для многоканальных растров задаются в диапазоне от 0 до n(bands) - 1, где n(bands) – общее число каналов.

multibandRas[0,2,2]
out: 4

Обработка значений вне границ

Для всех растров определяется тип пиксела и соответствующая битовая глубина. См. Емкость битовой глубины пикселов набора растровых данных для получения полного списка битовой глубины и диапазона значений, которые может содержать каждая ячейка. Если ячейке растра назначается значение, которое выходит за пределы диапазона, соответствующего битовой глубине, возникает ситуация значения вне границ. В таких ситуация ячейкам назначаются значения NoData. Например, тип пиксела растрового объекта myRas1'U8', то есть 8 бит без знака. Растр может содержать целочисленные значения в диапазоне от 0 до 255. Если ячейке растра myRas1 назначается значение 260, то ячейке будет присвоено значение NoData. Соответственно, при запросе значения для этой ячейки, будет возвращен ответ 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

Связанные разделы