Как получить координаты XY и значение ячейки каждого пикселя в растре с помощью Python?

16

Я действительно новичок в Python и хотел бы знать, существует ли быстрый метод получения значений ячеек растрового пикселя за пикселем и координат (координата XY центра каждого пикселя) с использованием Python в ArcGIS 10?

Чтобы описать это далее, мне нужно получить карту X, карту Y и значение ячейки первого пикселя, назначить эти три значения трем переменным и повторить этот шаг для остальных остальных пикселей (цикл по всему растру).


Я думаю, что мне нужно описать мой вопрос больше. Проблема в том, что мне нужно получить XY-расположение пикселя первого растра и получить значения ячеек нескольких других растров, соответствующих этому XY-расположению. Этот процесс должен проходить по циклу через каждый пиксель первого растра без создания шейп-файла промежуточной точки, так как он будет действительно очень трудоемким, поскольку мне приходится обрабатывать растр с почти 8 миллиардами пикселей. Кроме того, мне нужно сделать это с помощью Python в ArcGIS 10.

@JamesS: Большое спасибо за ваше предложение. Да, это будет работать для одного растра, но мне нужно также собрать значения ячеек для нескольких других растров. Проблема в том, что после получения координат X и Y первого пикселя первого растра мне нужно получить значение ячейки второго растра, соответствующее этому X, Y местоположению первого растра, затем третьего растра и так далее. Итак, я думаю, что при циклическом прохождении первого растра получение X и Y местоположения пикселя и получение значений ячеек другого растра, соответствующего этому местоположению, должно выполняться одновременно, но я не уверен. Это можно сделать, преобразовав первый растр в шейп-файл точки и выполнив Извлечение многозначностей в функцию точки в ArcGIS 10, но я

@hmfly: Спасибо, да, этот метод (RastertoNumpyarray) будет работать, если я смогу получить координату известного значения строки и столбца массива.

@whuber: я не хочу выполнять какие-либо вычисления, все, что мне нужно сделать, это записать координаты XY и значения ячеек в текстовый файл, и это все

Тире
источник
Может быть, вы просто хотите сделать математику для всего растра? Растровые калькуляторы работают попиксельно.
BWill
1
пожалуйста, опишите вашу цель более подробно.
BWill
Обычно эффективные и надежные решения получаются с помощью операций алгебры карт, а не зацикливания на точках. Ограничения в реализации алгебры карт Spatial Analyst не позволяют этому подходу работать в каждом случае, но в удивительно большом количестве ситуаций вам не нужно кодировать цикл. Какой именно расчет нужно выполнить?
whuber
Повторное редактирование: конечно, это законная цель. Формат может быть навязан вам необходимостью программного обеспечения в дальнейшем. Но учитывая, что для записи 8 миллиардов (X, Y, value1, ..., value3) кортежей потребуется от 224 миллиардов байтов (в двоичном формате) до, возможно, 400 миллиардов байтов (в ASCII), каждый из которых является довольно большим набором данных, возможно, стоит найти альтернативные подходы к тому, чего вы в конечном итоге пытаетесь достичь!
whuber

Ответы:

11

Следуя идее @ Dango, я создал и протестировал (на небольших растрах с одинаковым экстентом и размером ячейки) следующий код:

import arcpy, numpy

inRaster = r"C:\tmp\RastersArray.gdb\InRaster"
inRaster2 = r"C:\tmp\RastersArray.gdb\InRaster2"

##Get properties of the input raster
inRasterDesc = arcpy.Describe(inRaster)

#coordinates of the lower left corner
rasXmin = inRasterDesc.Extent.Xmin
rasYmin = inRasterDesc.Extent.Ymin

# Cell size, raster size
rasMeanCellHeight = inRasterDesc.MeanCellHeight
rasMeanCellWidth = inRasterDesc.MeanCellWidth
rasHeight = inRasterDesc.Height
rasWidth = inRasterDesc.Width

##Calculate coordinates basing on raster properties
#create numpy array of coordinates of cell centroids
def rasCentrX(rasHeight, rasWidth):
    coordX = rasXmin + (0.5*rasMeanCellWidth + rasWidth)
    return coordX
inRasterCoordX = numpy.fromfunction(rasCentrX, (rasHeight,rasWidth)) #numpy array of X coord

def rasCentrY(rasHeight, rasWidth):
    coordY = rasYmin + (0.5*rasMeanCellHeight + rasHeight)
    return coordY
inRasterCoordY = numpy.fromfunction(rasCentrY, (rasHeight,rasWidth)) #numpy array of Y coord

#combine arrays of coordinates (although array for Y is before X, dstack produces [X, Y] pairs)
inRasterCoordinates = numpy.dstack((inRasterCoordY,inRasterCoordX))


##Raster conversion to NumPy Array
#create NumPy array from input rasters 
inRasterArrayTopLeft = arcpy.RasterToNumPyArray(inRaster)
inRasterArrayTopLeft2 = arcpy.RasterToNumPyArray(inRaster2)

#flip array upside down - then lower left corner cells has the same index as cells in coordinates array
inRasterArray = numpy.flipud(inRasterArrayTopLeft)
inRasterArray2 = numpy.flipud(inRasterArrayTopLeft2)


# combine coordinates and value
inRasterFullArray = numpy.dstack((inRasterCoordinates, inRasterArray.T))

#add values from second raster
rasterValuesArray = numpy.dstack((inRasterFullArray, inRasterArray2.T))

На основе кода @hmfly вы можете получить доступ к нужным значениям:

(height, width, dim )=rasterValuesArray.shape
for row in range(0,height):
    for col in range(0,width):
        #now you have access to single array of values for one cell location

К сожалению, есть одно «но» - код подходит для массивов NumPy, которые могут обрабатываться системной памятью. Для моей системы (8 ГБ) самый большой массив был около 9000,9000.

Поскольку мой опыт не позволяет мне предоставлять дополнительную помощь, вы можете рассмотреть некоторые предложения по работе с большими массивами: /programming/1053928/python-numpy-very-large-matrices

arcpy.RasterToNumPyArrayметод позволяет указать подмножество растра, преобразованного в массив NumPy ( страница справки ArcGIS10 ), что может быть полезно при разбиении большого набора данных на подматрицы.

Marcin
источник
Код Марцина супер! спасибо, но он не записывает X, Y растра с тем же разрешением растра, я имею в виду, что x и y растут на 1 м, а не, например) на 100 метров .... У вас есть предложение исправить что Спасибо
7

Если вы просто хотите получить значения пикселей (строка, столбец), вы можете написать скрипт arcpy так:

import arcpy
raster = arcpy.Raster("yourfilepath")
array = arcpy.RasterToNumPyArray(raster)
(height, width)=array.shape
for row in range(0,height):
    for col in range(0,width):
        print str(row)+","+str(col)+":"+str(array.item(row,col))

Но если вы хотите получить координату пикселя, NumPyArray вам не поможет. Вы можете преобразовать растр в точку с помощью RasterToPoint Tool, а затем вы можете получить координату по форме Shape.

hmfly
источник
7

Самый простой способ вывода координат и значений ячеек в текстовый файл в ArcGIS 10 - это функция примера , не требующая кода и особенно не нуждающаяся в цикле по каждой ячейке. В ArcGIS <= 9.3x растровом калькуляторе раньше было так просто, как если outfile.csv = sample(someraster)бы он выводил текстовый файл со всеми (ненулевыми) значениями и координатами ячейки (в формате z, x, y). В ArcGIS 10 похоже, что аргумент «in_location_data» теперь является обязательным, поэтому вам нужно использовать синтаксис Sample(someraster, someraster, outcsvfile).

Изменить: Вы также можете указать несколько растров: Sample([someraster, anotherraster, etc], someraster, outcsvfile). Будет ли это работать на 8 миллиардов ячеек, у меня нет идеи ...

Редактировать: Обратите внимание, я не проверял это в ArcGIS 10, но использовал функцию примера в течение многих лет в <= 9.3 (и на рабочей станции).

Редактировать: сейчас я протестировал в ArcGIS 10, и он не будет выводиться в текстовый файл. Инструмент автоматически изменяет расширение файла на «.dbf». Однако ... следующий код Python работает как операторы алгебры карт SOMA и MOMA , все еще поддерживаются в ArcGIS 10:

import arcgisscripting
gp=arcgisscripting.create()
gp.multioutputmapalgebra(r'%s=sample(%s)' % (outputcsv,inputraster))
user2856
источник
Очень хорошо. Спасибо за указание на это - я раньше не замечал этот инструмент. Конечно, намного аккуратнее и проще, чем мое решение!
JamesS
6

Один из способов сделать это - использовать инструмент Raster_To_Point , а затем инструмент Add_XY_Coordinates . В итоге вы получите шейп-файл, где каждая строка в таблице атрибутов представляет пиксель вашего растра со столбцами для X_Coord , Y_Coord и Cell_Value . Затем вы можете зациклить эту таблицу с помощью курсора (или экспортировать ее в что-то вроде Excel, если хотите).

Если у вас есть только один растр для обработки, он, вероятно, не стоит создавать сценарии - просто используйте инструменты из ArcToolbox. Если вам нужно сделать это для многих растров, вы можете попробовать что-то вроде этого:

[ Примечание: у меня нет ArcGIS 10 и я не знаком с ArcPy, так что это просто очень грубый набросок. Он не проверен и почти наверняка нуждается в настройке, чтобы заставить его работать.]

import arcpy, os
from arcpy import env

# User input
ras_fold = r'path/to/my/data'           # The folder containing the rasters
out_fold = r'path/to/output/shapefiles' # The folder in which to create the shapefiles

# Set the workspace
env.workspace = ras_fold

# Get a list of raster datasets in the raster folder
raster_list = arcpy.ListRasters("*", "All")

# Loop over the rasters
for raster in raster_list:
    # Get the name of the raster dataset without the file extension
    dataset_name = os.path.splitext(raster)[0]

    # Build a path for the output shapefile
    shp_path = os.path.join(out_fold, '%s.shp' % dataset_name)

    # Convert the raster to a point shapefile
    arcpy.RasterToPoint_conversion(raster, shp_path, "VALUE")

    # Add columns to the shapefile containing the X and Y co-ordinates
    arcpy.AddXY_management(shp_path)

Затем вы можете перебирать таблицы атрибутов shapefile, используя курсор поиска, или (возможно, проще), используя dbfpy . Это позволит вам считывать данные из вашего растра (теперь хранящиеся в таблице .dbf в шейп-файле) в переменные python.

from dbfpy import dbf

# Path to shapefile .dbf
dbf_path = r'path\to\my\dbf_file.dbf'

# Open the dbf file
db = dbf.Dbf(dbf_path)

# Loop over the records
for rec in db:
    cell_no = rec['POINTID'] # Numbered from top left, running left to right along each row
    cell_x = rec['POINT_X']
    cell_y = rec['POINT_Y']
    cell_val = rec['GRID_CODE']

    # Print values
    print cell_no, cell_x, cell_y, cell_val
Jamess
источник
3

Может быть, вы могли бы создать мировой файл для растра, преобразовать растр в пустой массив. затем, если вы зациклите массив, вы получите значения ячеек, и если вы постепенно обновите x, y из файла мира, у вас также будут координаты для каждого значения ячейки. надеюсь, что это полезно.

данго
источник
Если вам не интересен метод инструмента Raster to Point, предложенный JamesS, я бы сказал, что это правильный путь.
nmpeterson
3

Код Марцина работал нормально, за исключением проблемы в функциях rasCentrX и rasCentrY, которая приводила к тому, что выходные координаты появлялись с другим разрешением (как заметил Грация). Мое решение было изменить

coordX = rasXmin + (0.5*rasMeanCellWidth + rasWidth)

в

coordX = rasXmin + ((0.5 + rasWidth) * rasMeanCellWidth)

и

  coordY = rasYmin + (0.5*rasMeanCellHeight + rasHeight)

в

  coordY = rasYmin + ((0.5 + rasHeight) * rasMeanCellHeight)

Я использовал код для преобразования сетки ESRI в файл CSV. Это было достигнуто путем удаления ссылки на inRaster2, а затем использования csv.writer для вывода координат и значений:

out = csv.writer(open(outputCSV,"wb"), delimiter=',', quoting=csv.QUOTE_NONNUMERIC)
out.writerow(['X','Y','Value'])
(height, width, dim )=inRasterFullArray.shape
for row in range(0,height):
    for col in range(0,width):
        out.writerow(inRasterFullArray[row,col])

Я также не нашел, что транспонирование было необходимо в

inRasterFullArray = numpy.dstack((inRasterCoordinates, inRasterArray.T))

так преобразовал это в

inRasterFullArray = numpy.dstack((inRasterCoordinates, inRasterArray))
Дэвид
источник
2

Уродливый, но очень эффективный:

  1. Создайте новый точечный объект с 4 точками за углами рассматриваемого растра. Убедитесь, что в той же системе координат, что и рассматриваемый растр.
  2. Добавьте двойные поля 'xcor' и 'ycor'
  3. Вычислить геометрию, чтобы получить координаты для этих полей
  4. Пространственный аналитик-> Интерполяция-> Тренд -> Линейная регрессия
  5. Настройки среды: привязать растр и размер ячейки к тому же, что и рассматриваемый растр
  6. Выполнить отдельно для «xcor» и «ycor»
  7. Выходят оценщики с координатами в качестве значений ячеек, которые используются в качестве входных данных для скриптов.
brokev03
источник
2

Простое решение с использованием пакетов Python с открытым исходным кодом:

import fiona
import rasterio
from pprint import pprint


def raster_point_coords(raster, points):

    # initialize dict to hold data
    pt_data = {}

    with fiona.open(points, 'r') as src:
        for feature in src:
            # create dict entry for each feature
            pt_data[feature['id']] = feature

    with rasterio.open(raster, 'r') as src:
        # read raster into numpy array
        arr = src.read()
        # rasterio always reads into 3d array, this is 2d, so reshape
        arr = arr.reshape(arr.shape[1], arr.shape[2])
        # get affine, i.e. data needed to work between 'image' and 'raster' coords
        a = src.affine

    for key, val in pt_data.items():
        # get coordinates
        x, y = val['geometry']['coordinates'][0], val['geometry']['coordinates'][1]
        # use affine to convert to row, column
        col, row = ~a * (x, y)
        # remember numpy array is indexed array[row, column] ie. y, x
        val['raster_value'] = arr[int(row), int(col)]

    pprint(pt_data) 

if __name__ == '__main__':
    # my Landsat raster
    ras = '/data01/images/sandbox/LT05_040028_B1.tif'
    # my shapefile with two points which overlap raster area
    pts = '/data01/images/sandbox/points.shp'
    # call function
    raster_point_coords(ras, pts)

Fiona удобна тем, что вы можете открывать шейп-файл, перебирать объекты и (как у меня) добавлять их к dictобъекту. Действительно, сама Фиона featureпохожа наdict тоже нее, поэтому к ней легко получить доступ. Если бы у моих точек были какие-либо атрибуты, они появлялись бы в этой записи вместе с координатами, идентификатором и т. Д.

Растерио удобно, потому что его легко читать в растре как простой массив, легкий и быстрый тип данных. У нас также есть доступ к a dictсвойств растра, включая affineвсе данные, которые нам нужны для преобразования координат x, y растра в строку массива, координаты col. Смотрите отличное объяснение @ Perrygeo здесь .

Мы получаем pt_dataтип, dictкоторый имеет данные для каждой точки и извлеченные данные raster_value. Мы можем легко переписать шейп-файл с извлеченными данными, если захотим.

dgketchum
источник