Bagaimana cara mendapatkan koordinat XY dan nilai sel setiap piksel dalam raster menggunakan Python?

16

Saya benar-benar baru untuk Python dan saya ingin tahu apakah ada metode cepat untuk mendapatkan nilai sel pixel raster oleh pixel dan koordinat (peta koordinat XY pusat setiap pixel) menggunakan Python di ArcGIS 10?

Untuk menjelaskan ini lebih lanjut, saya perlu mendapatkan peta X, peta Y dan nilai sel dari piksel pertama dan menetapkan tiga nilai tersebut ke dalam tiga variabel dan ulangi langkah ini untuk sisa piksel lainnya (loop melalui seluruh raster).


Saya pikir saya perlu lebih menjelaskan pertanyaan saya. Masalahnya adalah, saya perlu mendapatkan lokasi XY piksel dari raster pertama dan mendapatkan nilai sel beberapa raster lain yang sesuai dengan lokasi XY tersebut. Proses ini harus berulang melalui setiap piksel dari raster pertama tanpa membuat shapefile titik menengah karena akan benar-benar memakan waktu karena saya harus menangani raster dengan hampir 8 miliar piksel. Juga, saya perlu melakukan ini menggunakan Python di ArcGIS 10.

@ James: Terima kasih banyak atas saran Anda. Ya ini akan bekerja untuk satu raster tetapi saya perlu mengumpulkan nilai sel untuk beberapa raster lainnya juga. Masalahnya adalah, setelah mendapatkan koordinat X dan Y dari piksel pertama raster pertama, saya perlu mendapatkan nilai sel raster kedua yang sesuai dengan X, lokasi Y dari raster pertama, lalu raster ketiga dan seterusnya. Jadi, saya pikir ketika perulangan melalui raster pertama, mendapatkan lokasi X dan Y dari sebuah piksel dan mendapatkan nilai sel dari raster lain yang sesuai dengan lokasi itu harus dilakukan secara bersamaan tetapi saya tidak yakin. Ini dapat dilakukan dengan mengonversi raster pertama menjadi titik shapefile dan melakukan Extractivivivalues ​​to point function di ArcGIS 10 tetapi saya

@ hmfly: Terima kasih, Ya metode ini (RastertoNumpyarray) akan berfungsi jika saya bisa mendapatkan koordinat dari nilai baris dan kolom array yang diketahui.

@whuber: Saya tidak ingin melakukan perhitungan apa pun, yang perlu saya lakukan adalah menulis koordinat XY dan nilai sel ke dalam file teks dan itu saja

Berlari
sumber
Mungkin Anda hanya ingin melakukan perhitungan matematika secara keseluruhan? Kalkulator raster bekerja piksel demi piksel.
BWill
1
tolong jelaskan tujuan Anda secara lebih rinci.
BWill
Biasanya, solusi yang efisien dan andal diperoleh dengan menggunakan operasi Peta Aljabar daripada mengulang poin. Keterbatasan dalam implementasi aljabar peta Spatial Analyst menghalangi pendekatan ini dari bekerja dalam setiap kasus, tetapi dalam sejumlah besar situasi yang mengejutkan Anda tidak harus membuat kode loop. Perhitungan apa yang perlu Anda lakukan, tepatnya?
whuber
Re edit Anda: tentu saja itu tujuan yang sah. Format dapat dikenakan pada Anda oleh kebutuhan perangkat lunak lebih lanjut dalam pipa. Tetapi mengingat bahwa penulisan 8 miliar (X, Y, value1, ..., value3) tupel akan membutuhkan antara 224 miliar byte (dalam biner) dan mungkin 400 miliar byte (dalam ASCII), yang salah satunya adalah dataset yang agak besar, itu mungkin layak untuk menemukan pendekatan alternatif untuk apa pun yang akhirnya Anda capai!
whuber

Jawaban:

11

Mengikuti ide @ Dango yang saya buat dan uji (pada raster kecil dengan tingkat dan ukuran sel yang sama) kode berikut:

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

Berdasarkan kode @hmfly, Anda dapat memiliki akses ke nilai yang diinginkan:

(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

Sayangnya ada satu 'tetapi' - kode ini tepat untuk array NumPy yang dapat ditangani oleh memori sistem. Untuk sistem saya (8GB), array terbesar adalah sekitar 9000.9000.

Karena pengalaman saya tidak memungkinkan saya memberikan bantuan lebih lanjut, Anda dapat mempertimbangkan beberapa saran tentang berurusan dengan array besar: /programming/1053928/python-numpy-very-large-matrices

arcpy.RasterToNumPyArraymetode memungkinkan untuk menentukan subset dari raster yang dikonversi menjadi array NumPy ( halaman bantuan ArcGIS10 ) apa yang dapat berguna ketika memotong dataset besar menjadi submatrices.

Marcin
sumber
Kode Marcin super! terima kasih, tetapi tidak menulis X, Y dari raster dengan resolusi raster yang sama yang saya maksud x dan y tumbuh 1 m dan tidak, misalnya) 100 meter .... Apakah Anda memiliki saran untuk memperbaikinya? Terima kasih
7

Jika Anda hanya ingin mendapatkan nilai piksel melalui (baris, kolom), Anda dapat menulis skrip arcpy seperti ini:

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

Tetapi, jika Anda ingin mendapatkan koordinat piksel, NumPyArray tidak dapat membantu Anda. Anda bisa mengonversi raster ke titik dengan RasterToPoint Tool, dan kemudian Anda bisa mendapatkan koordinat dengan Shape yang diajukan.

hmfly
sumber
7

Metode paling sederhana untuk menghasilkan koordinat dan nilai sel ke file teks di ArcGIS 10 adalah fungsi sampel , tidak perlu kode dan terutama tidak perlu untuk mengulang setiap sel. Dalam ArcGIS <= 9,3x kalkulator raster, ia semudah outfile.csv = sample(someraster)yang akan menghasilkan file teks dari semua nilai dan koordinat sel (bukan nol) (dalam format z, x, y). Di ArcGIS 10, sepertinya argumen "in_location_data" sekarang wajib, jadi Anda perlu menggunakan sintaks Sample(someraster, someraster, outcsvfile).

Edit: Anda juga dapat menetapkan beberapa raster: Sample([someraster, anotherraster, etc], someraster, outcsvfile). Apakah ini akan bekerja pada 8 miliar sel, saya punya ide nooo ...

Sunting: Catatan, saya belum menguji ini di ArcGIS 10, tetapi telah menggunakan fungsi sampel selama bertahun-tahun dalam <= 9.3 (dan Workstation).

Sunting: Saya sekarang telah menguji di ArcGIS 10 dan tidak akan menghasilkan file teks. Alat mengubah ekstensi file menjadi ".dbf" secara otomatis. Namun ... kode python berikut berfungsi sebagai pernyataan aljabar peta SOMA dan MOMA masih didukung di ArcGIS 10:

import arcgisscripting
gp=arcgisscripting.create()
gp.multioutputmapalgebra(r'%s=sample(%s)' % (outputcsv,inputraster))
pengguna2856
sumber
Sangat bagus. Terima kasih telah menunjukkan ini - saya belum melihat alat ini sebelumnya. Tentunya jauh lebih rapi dan sederhana dari solusi saya!
JamesS
6

Salah satu cara untuk melakukan ini adalah dengan menggunakan alat Raster_To_Point diikuti oleh alat Add_XY_Coordinates . Anda akan berakhir dengan shapefile di mana setiap baris dalam tabel atribut mewakili piksel dari raster Anda dengan kolom untuk X_Coord , Y_Coord dan Cell_Value . Anda kemudian dapat mengulangi tabel ini menggunakan kursor (atau mengekspornya ke sesuatu seperti Excel jika Anda mau).

Jika Anda hanya memiliki satu raster untuk diproses, itu mungkin skrip tidak layak - cukup gunakan alat dari ArcToolbox. Jika Anda perlu melakukan ini untuk banyak raster, Anda dapat mencoba sesuatu seperti ini:

[ Catatan: Saya tidak punya ArcGIS 10 dan saya tidak terbiasa dengan ArcPy, jadi ini hanya garis besar yang sangat kasar. Ini belum teruji dan hampir pasti membutuhkan penyesuaian untuk membuatnya bekerja.]

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)

Anda kemudian dapat mengulangi tabel atribut shapefile menggunakan kursor pencarian atau (mungkin lebih sederhana) menggunakan dbfpy . Ini akan memungkinkan Anda untuk membaca data dari raster Anda (sekarang disimpan dalam tabel .dbf shapefile) menjadi variabel 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
sumber
3

Mungkin Anda bisa membuat file dunia untuk raster, rahasia raster ke array numpy. maka jika Anda mengulang array, Anda akan mendapatkan nilai sel dan jika Anda semakin memperbarui x, y dari file dunia Anda juga akan memiliki koordinat untuk setiap nilai sel. Semoga bermanfaat.

dango
sumber
Jika Anda tidak tertarik dengan metode alat Raster to Point yang disarankan oleh JamesS, saya akan mengatakan ini adalah cara yang harus dilakukan.
nmpeterson
3

Kode Marcin bekerja dengan baik kecuali masalah pada fungsi rasCentrX dan rasCentrY menyebabkan koordinat ouput muncul pada resolusi yang berbeda (seperti yang diamati oleh Grazia). Perbaikan saya adalah untuk berubah

coordX = rasXmin + (0.5*rasMeanCellWidth + rasWidth)

untuk

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

dan

  coordY = rasYmin + (0.5*rasMeanCellHeight + rasHeight)

untuk

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

Saya menggunakan kode untuk mengonversi ESRI Grid ke file CSV. Ini dicapai dengan menghapus referensi ke inRaster2, lalu menggunakan csv.writer untuk menampilkan koordinat dan nilai-nilai:

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

Saya juga tidak menemukan transpose yang diperlukan di

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

jadi dikonversi ke

inRasterFullArray = numpy.dstack((inRasterCoordinates, inRasterArray))
David
sumber
2

Jelek tetapi sangat efektif:

  1. Buat fitur poin baru dengan 4 poin di luar sudut raster yang bersangkutan. Pastikan dalam sistem koordinat yang sama dengan raster yang dimaksud.
  2. Tambahkan bidang ganda 'xcor' dan 'ycor'
  3. Hitung geometri untuk mendapatkan koordinat untuk bidang ini
  4. Analis Tata Ruang-> Interpolasi-> Tren -> Regresi linier
  5. Pengaturan lingkungan: snap raster dan ukuran sel sama dengan raster yang dimaksud
  6. Jalankan secara terpisah untuk 'xcor' dan 'ycor'
  7. Keluar datang penilai dengan koordinat sebagai nilai sel, gunakan sebagai input untuk skrip.
brokev03
sumber
2

Solusi sederhana menggunakan paket python open source:

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 berguna karena Anda dapat membuka shapefile, beralih melalui fitur, dan (seperti yang saya miliki) menambahkannya ke dictobjek. Memang Fiona featuresendiri seperti dictjuga, sehingga mudah untuk mengakses properti. Jika poin saya memiliki atribut, mereka akan muncul di dikt ini bersama dengan koordinat, id, dll.

Rasterio berguna karena mudah dibaca dalam raster sebagai array yang numpy, tipe data yang ringan dan cepat. Kami juga memiliki akses ke dictproperti raster termasuk affine, yang merupakan semua data yang kami butuhkan untuk mengonversi raster x, y koordinat menjadi baris array, koordinat col. Lihat penjelasan bagus @ perrygeo di sini .

Kita berakhir dengan pt_datatipe dictyang memiliki data untuk setiap titik dan diekstraksi raster_value. Kami dapat dengan mudah menulis ulang shapefile dengan data yang diekstraksi juga jika kami mau.

dgketchum
sumber