¿Cómo obtener coordenadas XY y valor de celda de cada píxel en un ráster usando Python?

16

Soy realmente nuevo en Python y me gustaría saber si hay un método rápido para obtener valores de celda de un ráster píxel por píxel y las coordenadas (mapa XY del centro de cada píxel) usando Python en ArcGIS 10.

Para describir esto más, necesito obtener el mapa X, el mapa Y y el valor de celda del primer píxel y asignar esos tres valores a tres variables y repetir este paso para el resto de los otros píxeles (recorrer todo el ráster).


Creo que necesito describir más mi pregunta. El problema es que necesito obtener la ubicación XY de un píxel del primer ráster y obtener los valores de celda de varios otros rásteres correspondientes a esa ubicación XY. Este proceso debe recorrer cada píxel del primer ráster sin crear ningún archivo de forma de punto intermedio, ya que realmente requerirá mucho tiempo ya que tengo que manejar un ráster con casi 8 mil millones de píxeles. Además, necesito hacer esto usando Python en ArcGIS 10.

@ James: Muchas gracias por su sugerencia. Sí, esto funcionaría para un ráster, pero también necesito recopilar los valores de celda para varios otros rásteres. El problema es que, después de obtener la coordenada X e Y del primer píxel del primer ráster, necesito obtener el valor de celda del segundo ráster correspondiente a esa ubicación X, Y del primer ráster, luego el tercer ráster y así sucesivamente. Entonces, creo que al recorrer el primer ráster, obtener la ubicación X e Y de un píxel y obtener los valores de celda del otro ráster correspondiente a esa ubicación debe hacerse simultáneamente, pero no estoy seguro. Esto se puede hacer convirtiendo el primer ráster en un archivo de forma de punto y realizando Extraer múltiples valores para la función de punto en ArcGIS 10 pero I '

@hmfly: Gracias, sí, este método (RastertoNumpyarray) funcionará si puedo obtener la coordenada de un valor conocido de fila y columna de la matriz.

@whuber: no quiero realizar ningún cálculo, todo lo que necesito hacer es escribir las coordenadas XY y los valores de las celdas en un archivo de texto y eso es todo

Guión
fuente
¿Quizás solo quieres hacer un poco de matemática en toda la trama? Las calculadoras ráster funcionan píxel por píxel.
BWill
1
Describa su propósito con más detalle.
BWill
Normalmente, se obtienen soluciones eficientes y confiables utilizando operaciones de Álgebra de mapas en lugar de recorrer puntos. Las limitaciones en la implementación de álgebra de mapas de Spatial Analyst impiden que este enfoque funcione en todos los casos, pero en un número sorprendentemente grande de situaciones no es necesario codificar un bucle. ¿Qué cálculo necesitas realizar exactamente?
whuber
Re su edición: por supuesto que es un propósito legítimo. El formato puede ser impuesto por las necesidades de software más adelante. Pero teniendo en cuenta que escribir 8 mil millones (X, Y, valor1, ..., valor3) tuplas requerirá entre 224 mil millones de bytes (en binario) y quizás 400 mil millones de bytes (en ASCII), cualquiera de los cuales es un conjunto de datos bastante grande, ¡Puede valer la pena encontrar enfoques alternativos para lo que sea que esté tratando de lograr!
whuber

Respuestas:

11

Siguiendo la idea de @ Dango, creé y probé (en pequeños rásteres con la misma extensión y tamaño de celda) el siguiente código:

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

Según el código @hmfly, puede tener acceso a los valores deseados:

(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

Desafortunadamente, hay un 'pero': el código es correcto para los arreglos NumPy que pueden ser manejados por la memoria del sistema. Para mi sistema (8 GB), la matriz más grande era de aproximadamente 9000,9000.

Como mi experiencia no me permite proporcionar más ayuda, puede considerar algunas sugerencias sobre cómo tratar con matrices grandes: /programming/1053928/python-numpy-very-large-matrices

arcpy.RasterToNumPyArrayEl método permite especificar el subconjunto de ráster convertido a matriz NumPy ( página de ayuda de ArcGIS10 ), lo que puede ser útil al fragmentar grandes conjuntos de datos en submatrices.

Marcin
fuente
El código de Marcin es super! gracias, pero no escribe la X, Y de la trama con la misma resolución de la trama, quiero decir que la x e y crecen 1 my no, por ejemplo) 100 metros ... ¿Tiene alguna sugerencia para arreglar que gracias
7

Si solo desea obtener los valores de píxeles (fila, columna), puede escribir un script arcpy como este:

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

Pero, si desea obtener la coordenada del píxel, NumPyArray no puede ayudarlo. Puede convertir el ráster en punto mediante la herramienta RasterToPoint, y luego puede obtener la coordenada por Shape archivada.

hmfly
fuente
7

El método más simple para generar coordenadas y valores de celda en un archivo de texto en ArcGIS 10 es la función de muestra , sin necesidad de código y especialmente sin necesidad de recorrer cada celda. En ArcGIS <= 9.3x calculadora ráster, solía ser tan simple como lo outfile.csv = sample(someraster)que generaría un archivo de texto de todos los valores y coordenadas (no nulos) de las celdas (en formato z, x, y). En ArcGIS 10, parece que el argumento "in_location_data" ahora es obligatorio, por lo que debe usar la sintaxis Sample(someraster, someraster, outcsvfile).

Editar: También puede especificar varios rásteres: Sample([someraster, anotherraster, etc], someraster, outcsvfile). Si esto funcionaría en 8 mil millones de células, no tengo ni idea ...

Editar: Tenga en cuenta que no he probado esto en ArcGIS 10, pero he usado la función de muestra durante años en <= 9.3 (y estación de trabajo).

Editar: ahora lo he probado en ArcGIS 10 y no se enviará a un archivo de texto. La herramienta cambia la extensión del archivo a ".dbf" automáticamente. Sin embargo ... el siguiente código de Python funciona ya que las declaraciones de álgebra de mapas SOMA y MOMA todavía son compatibles con ArcGIS 10:

import arcgisscripting
gp=arcgisscripting.create()
gp.multioutputmapalgebra(r'%s=sample(%s)' % (outputcsv,inputraster))
usuario2856
fuente
Muy agradable. Gracias por señalar esto: no había notado esta herramienta antes. ¡Ciertamente mucho más ordenado y simple que mi solución!
JamesS
6

Una forma de hacerlo sería utilizar la herramienta Raster_To_Point seguida de la herramienta Add_XY_Coordinates . Terminará con un archivo de forma donde cada fila de la tabla de atributos representa un píxel de su ráster con columnas para X_Coord , Y_Coord y Cell_Value . Luego puede recorrer esta tabla con un cursor (o exportarla a algo como Excel si lo prefiere).

Si solo tiene que procesar un ráster, probablemente no valga la pena crear scripts, solo use las herramientas de ArcToolbox. Si necesita hacer esto para muchos rásteres, puede intentar algo como esto:

[ Nota: no tengo ArcGIS 10 y no estoy familiarizado con ArcPy, así que este es solo un esbozo muy aproximado. No ha sido probado y seguramente necesitará ajustes para que funcione.]

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)

Luego puede recorrer las tablas de atributos de shapefile usando un cursor de búsqueda o (posiblemente más simple) usando dbfpy . Esto le permitirá leer los datos de su ráster (ahora almacenados en una tabla .dbf de shapefile) en variables de 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
fuente
3

Tal vez podría crear un archivo mundial para el ráster, convertir el ráster en una matriz numpy. luego, si realiza un bucle sobre la matriz, obtendrá los valores de celda y si actualiza cada vez más la x, y del archivo mundial, también tendrá las coordenadas para cada valor de celda. Espero que sea útil.

dango
fuente
Si no está interesado en el método de herramienta Raster to Point sugerido por JamesS, diría que este es el camino a seguir.
nmpeterson
3

El código de Marcin funcionó bien, excepto que un problema en las funciones rasCentrX y rasCentrY estaba causando que las coordenadas de salida aparecieran en una resolución diferente (como observó Grazia). Mi solución fue cambiar

coordX = rasXmin + (0.5*rasMeanCellWidth + rasWidth)

a

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

y

  coordY = rasYmin + (0.5*rasMeanCellHeight + rasHeight)

a

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

Usé el código para convertir una cuadrícula ESRI a un archivo CSV. Esto se logró eliminando la referencia a inRaster2, luego usando un csv.writer para generar las coordenadas y los valores:

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

Tampoco encontré que la transposición era necesaria en

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

tan convertido que a

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

Feo pero altamente efectivo:

  1. Cree una nueva característica de punto con 4 puntos fuera de las esquinas del ráster en cuestión. Asegúrese en el mismo sistema de coordenadas que el ráster en cuestión.
  2. Agregue los campos dobles 'xcor' y 'ycor'
  3. Calcular geometría para obtener coordenadas para estos campos
  4. Analista espacial-> Interpolación-> Tendencia -> Regresión lineal
  5. Configuración del entorno: ajustar el ráster y el tamaño de celda al mismo que el ráster en cuestión
  6. Ejecutar por separado para 'xcor' y 'ycor'
  7. Sale los evaluadores con coordenadas como valores de celda, utilícelos como entrada para los scripts.
brokev03
fuente
2

Una solución simple que utiliza paquetes python de código abierto:

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 es útil, ya que puede abrir un archivo de formas, recorrer las características y (como lo he hecho) agregarlas a un dictobjeto. De hecho, el Fiona en featuresí es como un dicttambién, por lo que es fácil acceder a las propiedades. Si mis puntos tuvieran algún atributo, aparecerían en este dict junto con las coordenadas, id, etc.

Rasterio es útil porque es fácil de leer en la trama como una matriz numpy, un tipo de datos ligero y rápido. También tenemos acceso a una dictde las propiedades de ráster affine, que incluye todos los datos que necesitamos para convertir las coordenadas ráster x, y en filas de matriz, coordenadas col. Vea la excelente explicación de @perrygeo aquí .

Terminamos con un pt_datatipo dictque tiene datos para cada punto y el extraído raster_value. También podríamos reescribir fácilmente el archivo de forma con los datos extraídos si quisiéramos.

dgketchum
fuente