Wie erhält man mit Python die XY-Koordinaten und den Zellenwert jedes Pixels in einem Raster?


16

Ich bin ein Neuling in Python und möchte wissen, ob es in ArcGIS 10 eine schnelle Methode gibt, um die Zellwerte eines Rasters Pixel für Pixel und die Koordinaten (XY-Koordinate der Pixelmitte zuordnen) mit Python abzurufen.

Um dies weiter zu beschreiben, muss ich die Karte X, die Karte Y und den Zellenwert des ersten Pixels abrufen und diese drei Werte drei Variablen zuweisen und diesen Schritt für den Rest der anderen Pixel wiederholen (Schleife durch das gesamte Raster).


Ich denke, ich muss meine Frage genauer beschreiben. Das Problem ist, dass ich die XY-Position eines Pixels des ersten Rasters und die Zellenwerte mehrerer anderer Raster abrufen muss, die dieser XY-Position entsprechen. Dieser Vorgang sollte durch jedes Pixel des ersten Rasters geschleift werden, ohne dass ein Zwischenpunkt-Shapefile erstellt wird, da er wirklich sehr zeitaufwendig ist, da ich ein Raster mit fast 8 Milliarden Pixeln verarbeiten muss. Ich muss dies auch mit Python in ArcGIS 10 tun.

@ JamesS: Vielen Dank für Ihren Vorschlag. Ja, dies würde für ein Raster funktionieren, aber ich muss die Zellenwerte auch für mehrere andere Raster erfassen. Das Problem besteht darin, dass ich nach dem Abrufen der X- und Y-Koordinate des ersten Pixels des ersten Rasters den Zellenwert des zweiten Rasters abrufen muss, der der X-, Y-Position des ersten Rasters, dann des dritten Rasters usw. entspricht. Wenn Sie also das erste Raster durchlaufen, sollten Sie die X- und Y-Position eines Pixels und die Zellwerte des anderen Rasters, die dieser Position entsprechen, gleichzeitig ermitteln, aber ich bin mir nicht sicher. Dies kann erreicht werden, indem das erste Raster in ein Punkt-Shapefile konvertiert und die Funktion "Mehrfachwerte in Punkt extrahieren" in ArcGIS 10 ausgeführt wird.

@hmfly: Danke, ja, diese Methode (RastertoNumpyarray) funktioniert, wenn ich die Koordinate eines bekannten Zeilen- und Spaltenwerts des Arrays ermitteln kann.

@whuber: Ich möchte keine Berechnungen durchführen, ich muss nur XY-Koordinaten und Zellenwerte in eine Textdatei schreiben und das ist alles


Vielleicht möchten Sie nur das gesamte Raster berechnen? Rasterrechner arbeiten pixelweise.
Bis zum

1
Bitte beschreiben Sie Ihren Zweck ausführlicher.
Bis zum

Normalerweise werden effiziente und zuverlässige Lösungen durch die Verwendung von Map Algebra-Operationen erzielt, anstatt eine Schleife über Punkte zu erstellen. Einschränkungen in der Kartenalgebra-Implementierung von Spatial Analyst verhindern, dass dieser Ansatz in jedem Fall funktioniert, aber in einer überraschend großen Anzahl von Situationen müssen Sie keine Schleife codieren. Welche Berechnung müssen Sie genau durchführen?
Whuber

Re your edit: das ist natürlich ein legitimer zweck. Das Format kann Ihnen durch die Anforderungen der Software weiter unten in der Pipeline auferlegt werden. Aber wenn man bedenkt, dass das Schreiben von 8 Milliarden (X, Y, Wert1, ..., Wert3) Tupeln zwischen 224 Milliarden Bytes (in Binärform) und vielleicht 400 Milliarden Bytes (in ASCII) erfordert Vielleicht lohnt es sich, alternative Ansätze für das zu finden, was Sie letztendlich erreichen wollen!
Whuber

Antworten:


11

Nach der Idee von @ Dango habe ich den folgenden Code erstellt und getestet (auf kleinen Rastern mit demselben Umfang und derselben Zellengröße):

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

Basierend auf dem @ hmfly-Code können Sie auf die gewünschten Werte zugreifen:

(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

Leider gibt es ein "aber" - der Code ist richtig für NumPy-Arrays, die vom Systemspeicher verarbeitet werden können. Für mein System (8 GB) betrug das größte Array etwa 9000.9000.

Da ich aufgrund meiner Erfahrung keine weitere Hilfe geben kann, können Sie einige Vorschläge zum Umgang mit großen Arrays berücksichtigen: /programming/1053928/python-numpy-very-large-matrices

arcpy.RasterToNumPyArrayMit dieser Methode können Sie die Teilmenge des in ein NumPy-Array konvertierten Rasters angeben ( ArcGIS10-Hilfeseite ). Dies kann hilfreich sein, wenn Sie große Datasets in Teilmatrizen aufteilen .


Marcins Code ist super! danke, aber es wird nicht das X, Y des Rasters mit der gleichen Auflösung des Rasters geschrieben. Ich meine, das X und das Y wachsen um 1 m und nicht zum Beispiel um 100 m dass Dank

7

Wenn Sie nur die Pixelwerte durch (Zeile, Spalte) erhalten möchten, können Sie ein Arcpy-Skript wie folgt schreiben:

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

Wenn Sie jedoch die Pixelkoordinate ermitteln möchten, kann Ihnen NumPyArray nicht weiterhelfen. Sie können das Raster mit dem RasterToPoint-Tool in einen Punkt konvertieren und anschließend die Koordinate nach Formfeld abrufen.


7

Die einfachste Methode zum Ausgeben von Koordinaten und Zellenwerten in eine Textdatei in ArcGIS 10 ist die Beispielfunktion , bei der kein Code und insbesondere keine Schleife über jede Zelle erforderlich ist. In ArcGIS <= 9.3x war es früher so einfach, outfile.csv = sample(someraster)eine Textdatei mit allen (nicht null) Zellenwerten und Koordinaten (im Format z, x, y) auszugeben. In ArcGIS 10 scheint das Argument "in_location_data" jetzt obligatorisch zu sein, sodass Sie die Syntax verwenden müssen Sample(someraster, someraster, outcsvfile).

Edit: Sie können auch mehrere Raster angeben: Sample([someraster, anotherraster, etc], someraster, outcsvfile). Ob das mit 8 Milliarden Zellen funktionieren würde, habe ich keine Ahnung ...

Bearbeiten: Beachten Sie, dass ich dies in ArcGIS 10 nicht getestet habe, aber die Beispielfunktion seit Jahren in <= 9.3 (und Workstation) verwendet habe.

Bearbeiten: Ich habe es jetzt in ArcGIS 10 getestet und es wird nicht in eine Textdatei ausgegeben. Das Tool ändert die Dateierweiterung automatisch in ".dbf". Der folgende Python-Code funktioniert jedoch als SOMA- und MOMA- Map-Algebra-Anweisungen, die in ArcGIS 10 weiterhin unterstützt werden:

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

Sehr schön. Vielen Dank für den Hinweis - ich hatte dieses Tool noch nie bemerkt. Sicher viel übersichtlicher und einfacher als meine Lösung!
JamesS

6

Eine Möglichkeit hierfür ist die Verwendung des Tools Raster_To_Point , gefolgt vom Tool Add_XY_Coordinates . Am Ende erhalten Sie ein Shapefile, in dem jede Zeile in der Attributtabelle ein Pixel aus Ihrem Raster mit Spalten für X_Coord , Y_Coord und Cell_Value darstellt . Sie können diese Tabelle dann mit einem Cursor durchlaufen (oder nach Belieben nach Excel exportieren).

Wenn Sie nur ein Raster verarbeiten müssen, lohnt es sich wahrscheinlich nicht, ein Skript zu erstellen. Verwenden Sie einfach die Tools von ArcToolbox. Wenn Sie dies für viele Raster ausführen müssen, können Sie Folgendes versuchen:

[ Hinweis: Ich habe kein ArcGIS 10 und bin nicht mit ArcPy vertraut. Dies ist also nur eine sehr grobe Darstellung. Es ist ungetestet und wird mit ziemlicher Sicherheit eine Feinabstimmung benötigen, um es zum Laufen zu bringen.]

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)

Sie können dann Schleife über die Shape - Datei Attributtabellen mit Hilfe eines Such Cursor oder (möglicherweise einfacher) mit dbfpy . Auf diese Weise können Sie die Daten aus Ihrem Raster (jetzt in einer Shapefile-DBF-Tabelle gespeichert) in Python-Variablen lesen.

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

Möglicherweise können Sie eine Weltdatei für das Raster erstellen und das Raster in ein numpy-Array konvertieren. Wenn Sie das Array durchlaufen, werden die Zellenwerte abgerufen, und wenn Sie in zunehmendem Maße das x, y aus der Weltdatei aktualisieren, erhalten Sie auch die Koordinaten für jeden Zellenwert. hoffe das ist nützlich.


Wenn Sie nicht an der von JamesS vorgeschlagenen Methode des Raster-zu-Punkt-Werkzeugs interessiert sind, ist dies der richtige Weg.
nmpeterson

3

Marcins Code funktionierte einwandfrei, außer dass ein Problem in den Funktionen rasCentrX und rasCentrY dazu führte, dass die Ausgabekoordinaten mit einer anderen Auflösung angezeigt wurden (wie Grazia beobachtete). Mein Fix war es, sich zu ändern

coordX = rasXmin + (0.5*rasMeanCellWidth + rasWidth)

zu

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

und

  coordY = rasYmin + (0.5*rasMeanCellHeight + rasHeight)

zu

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

Ich habe den Code verwendet, um ein ESRI-Grid in eine CSV-Datei zu konvertieren. Dies wurde erreicht, indem der Verweis auf inRaster2 entfernt und anschließend mit einem csv.writer die Koordinaten und Werte ausgegeben wurden:

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

Ich fand auch nicht, dass die Transponierung in gebraucht wurde

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

so umgewandelt, dass zu

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

2

Hässlich aber hochwirksam:

  1. Erstellen Sie ein neues Punkt-Feature mit 4 Punkten außerhalb der Ecken des betreffenden Rasters. Stellen Sie sicher, dass sich das betreffende Raster im selben Koordinatensystem befindet.
  2. Fügen Sie die Doppelfelder 'xcor' und 'ycor' hinzu
  3. Berechnen Sie die Geometrie, um die Koordinaten für diese Felder zu erhalten
  4. Spatial Analyst-> Interpolation-> Trend -> Lineare Regression
  5. Umgebungseinstellungen: Raster und Zellengröße auf das betreffende Raster einstellen
  6. Separat für 'xcor' und 'ycor' ausführen
  7. Herausgekommen sind Bewerter mit Koordinaten als Zellenwerte, die als Eingabe für Skripte verwendet werden.

2

Eine einfache Lösung mit Open Source-Python-Paketen:

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 ist praktisch, da Sie ein Shapefile öffnen, die Features durchlaufen und (wie ich es getan habe) an ein dictObjekt anhängen können . In der Tat ist die Fiona featureselbst wie eine dictebenso, so dass es einfach ist, auf Eigenschaften zuzugreifen. Wenn meine Punkte Attribute hätten, würden sie zusammen mit den Koordinaten, der ID usw. in diesem Diktat erscheinen.

Rasterio ist praktisch, da es sich im Raster leicht als Numpy-Array, als leichter und schneller Datentyp, lesen lässt. Wir haben auch Zugriff auf eine dictvon Rastereigenschaften, einschließlich der affine, die alle Daten sind, die wir zum Konvertieren der Raster-XY-Koordinaten in Array-Zeilen-Spalten-Koordinaten benötigen. Siehe @perrygeos ausgezeichnete Erklärung hier .

Am Ende haben wir einen pt_dataTyp, dictder Daten für jeden Punkt und den extrahierten enthält raster_value. Wir könnten das Shapefile auch leicht mit den extrahierten Daten umschreiben, wenn wir wollten.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.