Obtenga coordenadas y valores de píxeles correspondientes de GeoTiff usando Python gdal y guárdelos como matriz numpy

10

¿Cómo puedo obtener las coordenadas proyectadas, así como los valores de píxeles reales en esas coordenadas de un archivo GeoTiff y luego guardarlos en una matriz numpy? Tengo el archivo arsenci020l.tif, y sus coordenadas están en metros. A continuación se muestra la salida resumida de gdalinfo que ejecuté en él.

~$ gdalinfo arsenci020l.tif 
Driver: GTiff/GeoTIFF
Files: arsenci020l.tif
       arsenci020l.tfw
Size is 10366, 7273
Coordinate System is:
PROJCS["Lambert Azimuthal Equal Area projection with arbitrary plane grid; projection center 100.0 degrees W, 45.0 degrees N",
    GEOGCS["WGS 84",
        DATUM["WGS_1984",
            SPHEROID["WGS 84",6378137,298.257223563,
                AUTHORITY["EPSG","7030"]],
            AUTHORITY["EPSG","6326"]],
        PRIMEM["Greenwich",0],
        UNIT["degree",0.0174532925199433],
        AUTHORITY["EPSG","4326"]],
    PROJECTION["Lambert_Azimuthal_Equal_Area"],
    PARAMETER["latitude_of_center",45],
    PARAMETER["longitude_of_center",-100],
    PARAMETER["false_easting",0],
    PARAMETER["false_northing",0],
    UNIT["metre",1,
        AUTHORITY["EPSG","9001"]]]
Origin = (-6086629.000000000000000,4488761.000000000000000)
Pixel Size = (1000.000000000000000,-1000.000000000000000)
...

Hubo una pregunta similar aquí sobre la obtención de coordenadas lat / long de tiff (Obtener Latitud y Longitud de un Archivo GeoTIFF) y la respuesta mostró cómo obtener solo las coordenadas de píxel x e y superior izquierda. Necesito obtener TODAS las coordenadas de píxel proyectadas, así como obtener los valores de píxel y guardarlos en una matriz numpy. ¿Cómo puedo hacerlo?

irakhman
fuente
¿Quieres 10366 × 7273 = más de 75 millones de puntos?
Mike T
@MikeT Creo que sí, no sé realmente una mejor solución de cómo abordar el problema que estoy tratando de resolver: necesito encontrar la coordenada de píxeles más cercana de este conjunto de datos a cada centroide del bloque de EE. UU. Y luego asignar el valor de píxel correspondiente a ese bloque. Al buscar alrededor, me di cuenta de que la consulta cKDTree me ayudará con la búsqueda del vecino más cercano. La función de pitón para el algoritmo pide un "árbol" para consultar como matriz numpy. Para hacer un "árbol" De todas las coordenadas de píxeles de este conjunto de datos, necesito almacenarlas todas de alguna manera. Si tiene una mejor solución, ¡hágamelo saber!
irakhman

Respuestas:

7

agregaría como comentario, pero un poco largo, en caso de que quiera usar gdal / ogr dentro de python, algo como esto podría funcionar (pirateado desde otro código que tenía, ¡no probado!) Esto también supone que en lugar de encontrar el más cercano píxel de trama a un centroide de polígono, simplemente consulta el ráster en la xy del centroide. No tengo idea de cuál sería la compensación de velocidad ...

from osgeo import gdal,ogr

fc='PathtoYourVector'
rast='pathToYourRaster'

def GetCentroidValue(fc,rast):
    #open vector layer
    drv=ogr.GetDriverByName('ESRI Shapefile') #assuming shapefile?
    ds=drv.Open(fc,True) #open for editing
    lyr=ds.GetLayer(0)

    #open raster layer
    src_ds=gdal.Open(rast) 
    gt=src_ds.GetGeoTransform()
    rb=src_ds.GetRasterBand(1)
    gdal.UseExceptions() #so it doesn't print to screen everytime point is outside grid

    for feat in lyr:
        geom=feat.GetGeometryRef()
        mx=geom.Centroid().GetX()
        my=geom.Centroid().GetY()

        px = int((mx - gt[0]) / gt[1]) #x pixel
        py = int((my - gt[3]) / gt[5]) #y pixel
        try: #in case raster isnt full extent
            structval=rb.ReadRaster(px,py,1,1,buf_type=gdal.GDT_Float32) #Assumes 32 bit int- 'float'
            intval = struct.unpack('f' , structval) #assume float
            val=intval[0]
        except:
            val=-9999 #or some value to indicate a fail

       feat.SetField('YOURFIELD',val)
       lyr.SetFeature(feat)

    src_ds=None
    ds=None

GetCentroidValue(fc,rast)
movimiento fluido
fuente
14

Esto debería ponerte en marcha. Los valores ráster se leen usando rasterio , y las coordenadas del centro de píxeles se convierten en Este / Norte usando afín , que luego se convierten en Latitud / Longitud usando pyproj . La mayoría de las matrices tienen la misma forma que el ráster de entrada.

import rasterio
import numpy as np
from affine import Affine
from pyproj import Proj, transform

fname = '/path/to/your/raster.tif'

# Read raster
with rasterio.open(fname) as r:
    T0 = r.transform  # upper-left pixel corner affine transform
    p1 = Proj(r.crs)
    A = r.read()  # pixel values

# All rows and columns
cols, rows = np.meshgrid(np.arange(A.shape[2]), np.arange(A.shape[1]))

# Get affine transform for pixel centres
T1 = T0 * Affine.translation(0.5, 0.5)
# Function to convert pixel row/column index (from 0) to easting/northing at centre
rc2en = lambda r, c: (c, r) * T1

# All eastings and northings (there is probably a faster way to do this)
eastings, northings = np.vectorize(rc2en, otypes=[np.float, np.float])(rows, cols)

# Project all longitudes, latitudes
p2 = Proj(proj='latlong',datum='WGS84')
longs, lats = transform(p1, p2, eastings, northings)
Mike T
fuente
1
Cuando uso esto, recibo el mensaje "AttributeError: el objeto 'DatasetReader' no tiene el atributo 'affine'" para la línea "T0 = r.affine"
mitchus
@mitchus Aparentemente affinees solo un alias para transform, y el alias se ha eliminado de la versión más reciente de rasterio. Edité la respuesta, pero parece que debe ser revisada por pares ya que soy nuevo aquí. :)
Autumnsault
1
También parece que los índices son incorrectos A.shape, que tiene solo dos dimensiones.
Autumnsault