Come ottenere le coordinate XY e il valore della cella di ciascun pixel in un raster usando Python?


16

Sono veramente nuovo in Python e vorrei sapere se esiste un metodo rapido per ottenere i valori di cella di un pixel raster per pixel e le coordinate (mappa coordinate XY del centro di ciascun pixel) usando Python in ArcGIS 10?

Per descriverlo ulteriormente, devo ottenere la mappa X, la mappa Y e il valore della cella del primo pixel e assegnare quei tre valori a tre variabili e ripetere questo passaggio per il resto degli altri pixel (scorrere l'intero raster).


Penso di dover descrivere di più la mia domanda. Il problema è che devo ottenere la posizione XY di un pixel del primo raster e ottenere i valori di cella di molti altri raster corrispondenti a quella posizione XY. Questo processo dovrebbe essere ripetuto attraverso ogni pixel del primo raster senza creare alcun shapefile di punto intermedio in quanto richiederà davvero molto tempo poiché devo gestire un raster con quasi 8 miliardi di pixel. Inoltre, devo farlo usando Python in ArcGIS 10.

@JamesS: Grazie mille per il tuo suggerimento. Sì, questo funzionerebbe per un raster ma ho bisogno di raccogliere i valori delle celle anche per molti altri raster. Il problema è che, dopo aver ottenuto le coordinate X e Y del primo pixel del primo raster, devo ottenere il valore della cella del secondo raster corrispondente a quella posizione X, Y del primo raster, quindi del terzo raster e così via. Quindi, penso che quando si esegue il ciclo attraverso il primo raster, ottenere la posizione X e Y di un pixel e ottenere i valori di cella dell'altro raster corrispondenti a quella posizione dovrebbe essere fatto contemporaneamente ma non ne sono sicuro. Questo può essere fatto convertendo il primo raster in un file di forma punto ed eseguendo Estrai multivalori in funzione punto in ArcGIS 10 ma I '

@hmfly: Grazie, Sì, questo metodo (RastertoNumpyarray) funzionerà se riesco a ottenere le coordinate di una riga nota e il valore di una colonna dell'array.

@whuber: Non voglio eseguire calcoli, tutto quello che devo fare è scrivere coordinate XY e valori di cella in un file di testo e questo è tutto


Forse vuoi solo fare un po 'di matematica sull'intero raster? Le calcolatrici raster funzionano pixel per pixel.
B

1
descrivi il tuo scopo in modo più dettagliato.
B

Normalmente, si ottengono soluzioni efficienti e affidabili utilizzando le operazioni di Map Algebra anziché eseguire il loop dei punti. Le limitazioni nell'implementazione dell'algebra delle mappe di Analista spaziale impediscono a questo approccio di funzionare in ogni caso, ma in un numero sorprendentemente elevato di situazioni non è necessario codificare un ciclo. Quale calcolo devi eseguire esattamente?
whuber

Per quanto riguarda la tua modifica: ovviamente è uno scopo legittimo. Il formato può essere imposto dalle esigenze del software più avanti nella pipeline. Ma considerando che scrivere 8 tuple (X, Y, valore1, ..., valore3) richiederà tra 224 miliardi di byte (in binario) e forse 400 miliardi di byte (in ASCII), uno dei quali è un set di dati piuttosto grande, potrebbe valere la pena di trovare approcci alternativi a qualunque cosa tu stia cercando di realizzare!
whuber

Risposte:


11

Seguendo l'idea di @ Dango ho creato e testato (su piccoli raster con la stessa estensione e dimensione della cella) il seguente codice:

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

Basato sul codice @hmfly, puoi avere accesso ai valori desiderati:

(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

Sfortunatamente c'è un 'ma': il codice è giusto per gli array NumPy che possono essere gestiti dalla memoria di sistema. Per il mio sistema (8 GB), l'array più grande era di circa 9000.9000.

Poiché la mia esperienza non mi consente di fornire ulteriore aiuto, puoi prendere in considerazione alcuni suggerimenti su come gestire array di grandi dimensioni: /programming/1053928/python-numpy-very-large-matrices

arcpy.RasterToNumPyArrayIl metodo consente di specificare il sottoinsieme di raster convertito in array NumPy ( pagina della guida di ArcGIS10 ) cosa può essere utile quando si esegue il chunking di set di dati di grandi dimensioni in sottogruppi.


Il codice di Marcin è super! grazie, ma non scrive la X, Y del raster con la stessa risoluzione del raster intendo che xey crescono di 1 m e non, ad esempio) di 100 metri .... Hai un suggerimento da risolvere che grazie

7

Se vuoi solo ottenere i valori dei pixel (riga, colonna), puoi scrivere uno script arcpy come questo:

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

Ma, se vuoi ottenere le coordinate del pixel, NumPyArray non può aiutarti. È possibile convertire il raster in modo che punti dallo strumento RasterToPoint, quindi è possibile ottenere le coordinate in base alla forma.


7

Il metodo più semplice per generare coordinate e valori di cella in un file di testo in ArcGIS 10 è la funzione di esempio , non è necessario il codice e soprattutto non è necessario eseguire il ciclo su ogni cella. In ArcGIS <= 9.3x calcolatrice raster era semplice come quello outfile.csv = sample(someraster)che avrebbe prodotto un file di testo di tutti i valori (non nulli) delle celle e delle coordinate (nei formati z, x, y). In ArcGIS 10, sembra che l'argomento "in_location_data" sia ora obbligatorio, quindi è necessario utilizzare la sintassiSample(someraster, someraster, outcsvfile) .

Edit: Si può anche specificare più raster: Sample([someraster, anotherraster, etc], someraster, outcsvfile). Se questo funzionerebbe su 8 miliardi di celle, non ho idea ...

Modifica: Nota, non ho provato questo in ArcGIS 10, ma ho usato la funzione di esempio per anni in <= 9.3 (e Workstation).

Modifica: ora ho testato in ArcGIS 10 e non verrà generato in un file di testo. Lo strumento modifica automaticamente l'estensione del file in ".dbf". Tuttavia ... il seguente codice python funziona come le istruzioni di algebra delle mappe SOMA e MOMA sono ancora supportate in ArcGIS 10:

import arcgisscripting
gp=arcgisscripting.create()
gp.multioutputmapalgebra(r'%s=sample(%s)' % (outputcsv,inputraster))

Molto bella. Grazie per averlo sottolineato - non avevo notato questo strumento prima. Certamente molto più ordinato e più semplice della mia soluzione!
JamesS

6

Un modo per farlo sarebbe utilizzare lo strumento Raster_To_Point seguito dallo strumento Add_XY_Coordinates . Finirai con uno shapefile in cui ogni riga nella tabella degli attributi rappresenta un pixel del tuo raster con colonne per X_Coord , Y_Coord e Cell_Value . Puoi quindi scorrere su questa tabella usando un cursore (o esportarlo in qualcosa come Excel se preferisci).

Se hai solo un raster da elaborare, probabilmente non vale la pena fare script: usa semplicemente gli strumenti di ArcToolbox. Se è necessario farlo per molti raster, è possibile provare qualcosa del genere:

[ Nota: non ho ArcGIS 10 e non ho familiarità con ArcPy, quindi questo è solo un contorno molto approssimativo. Non è stato testato e avrà quasi sicuramente bisogno di modifiche per farlo funzionare.]

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)

È quindi possibile scorrere ciclicamente le tabelle degli attributi dello shapefile usando un cursore di ricerca o (possibilmente più semplice) usando dbfpy . Questo ti permetterà di leggere i dati dal tuo raster (ora archiviato in una tabella .dbf dello shapefile) in variabili 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

3

Forse potresti creare un file mondiale per il raster, convertire il raster in un array intorpidito. quindi se esegui il ciclo sull'array otterrai i valori della cella e se aggiorni in modo incrementale x, y dal file mondiale avrai anche le coordinate per ciascun valore di cella. spero che sia utile.


Se non sei interessato al metodo dello strumento Raster to Point suggerito da JamesS, direi che questa è la strada da percorrere.
nmpeterson

3

Il codice di Marcin ha funzionato bene, tranne un problema nelle funzioni rasCentrX e rasCentrY che stava facendo apparire le coordinate di uscita con una risoluzione diversa (come ha osservato Grazia). La mia soluzione era cambiare

coordX = rasXmin + (0.5*rasMeanCellWidth + rasWidth)

per

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

e

  coordY = rasYmin + (0.5*rasMeanCellHeight + rasHeight)

per

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

Ho usato il codice per convertire una griglia ESRI in un file CSV. Ciò è stato ottenuto rimuovendo il riferimento a inRaster2, quindi utilizzando un csv.writer per generare coordinate e valori:

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

Inoltre non ho trovato il recepimento necessario

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

così convertito in

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

2

Brutto ma molto efficace:

  1. Crea una nuova funzione punti con 4 punti fuori dagli angoli del raster in questione. Assicurarsi nello stesso sistema di coordinate del quadro in questione.
  2. Aggiungi i campi doppi "xcor" e "ycor"
  3. Calcola la geometria per ottenere le coordinate per questi campi
  4. Analista spaziale-> Interpolazione-> Tendenza -> regressione lineare
  5. Impostazioni ambiente: snap raster e dimensioni della cella uguali al raster in questione
  6. Eseguire separatamente per 'xcor' e 'ycor'
  7. Vengono fuori i raters con coordinate come valori di cella, usare come input per gli script.

2

Una soluzione semplice che utilizza pacchetti 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 è utile in quanto è possibile aprire un file di forma, scorrere le funzionalità e (come ho già fatto) aggiungerle a un dictoggetto. In effetti anche la featurestessa Fiona è come una dict, quindi è facile accedere alle proprietà. Se i miei punti avessero qualche attributo, apparirebbero in questo dict insieme alle coordinate, ID, ecc.

Rasterio è utile perché è facile da leggere nel raster come una matrice numpy, un tipo di dati leggero e veloce. Abbiamo anche accesso a una dictdelle proprietà raster incluso il affine, che è tutti i dati necessari per convertire le coordinate raster x, y in righe di array, coordinate col. Vedi l'eccellente spiegazione di @ perrygeo qui .

Finiamo con un pt_datatipo dictche ha i dati per ciascun punto e quello estratto raster_value. Potremmo facilmente riscrivere lo shapefile con i dati estratti, se lo desiderassimo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.