Come ottenere le coordinate degli angoli raster utilizzando i collegamenti GDAL di Python?


30

C'è un modo per ottenere le coordinate degli angoli (in gradi lat / long) da un file raster usando i collegamenti Python di gdal?

Alcune ricerche online mi hanno convinto che non c'è, quindi ho sviluppato un lavoro analizzando l'output di gdalinfo, è un po 'di base ma ho pensato che avrebbe potuto risparmiare un po' di tempo per le persone che potrebbero essere meno a proprio agio con Python. Funziona anche solo se gdalinfo contiene le coordinate geografiche insieme alle coordinate degli angoli, cosa che non credo sia sempre il caso.

Ecco la mia soluzione alternativa, qualcuno ha soluzioni migliori?

gdalinfo su un raster appropriato produce qualcosa di simile a metà dell'output:

Corner Coordinates:
Upper Left  (  -18449.521, -256913.934) (137d 7'21.93"E,  4d20'3.46"S)
Lower Left  (  -18449.521, -345509.683) (137d 7'19.32"E,  5d49'44.25"S)
Upper Right (   18407.241, -256913.934) (137d44'46.82"E,  4d20'3.46"S)
Lower Right (   18407.241, -345509.683) (137d44'49.42"E,  5d49'44.25"S)
Center      (     -21.140, -301211.809) (137d26'4.37"E,  5d 4'53.85"S)

Questo codice funzionerà su file che sembrano gdalinfo. Credo che a volte le coordinate siano in gradi e decimali, anziché in gradi, minuti e secondi; dovrebbe essere banale adattare il codice per quella situazione.

import numpy as np
import subprocess

def GetCornerCoordinates(FileName):
    GdalInfo = subprocess.check_output('gdalinfo {}'.format(FileName), shell=True)
    GdalInfo = GdalInfo.split('/n') # Creates a line by line list.
    CornerLats, CornerLons = np.zeros(5), np.zeros(5)
    GotUL, GotUR, GotLL, GotLR, GotC = False, False, False, False, False
    for line in GdalInfo:
        if line[:10] == 'Upper Left':
            CornerLats[0], CornerLons[0] = GetLatLon(line)
            GotUL = True
        if line[:10] == 'Lower Left':
            CornerLats[1], CornerLons[1] = GetLatLon(line)
            GotLL = True
        if line[:11] == 'Upper Right':
            CornerLats[2], CornerLons[2] = GetLatLon(line)
            GotUR = True
        if line[:11] == 'Lower Right':
            CornerLats[3], CornerLons[3] = GetLatLon(line)
            GotLR = True
        if line[:6] == 'Center':
            CornerLats[4], CornerLons[4] = GetLatLon(line)
            GotC = True
        if GotUL and GotUR and GotLL and GotLR and GotC:
            break
    return CornerLats, CornerLons 

def GetLatLon(line):
    coords = line.split(') (')[1]
    coords = coords[:-1]
    LonStr, LatStr = coords.split(',')
    # Longitude
    LonStr = LonStr.split('d')    # Get the degrees, and the rest
    LonD = int(LonStr[0])
    LonStr = LonStr[1].split('\'')# Get the arc-m, and the rest
    LonM = int(LonStr[0])
    LonStr = LonStr[1].split('"') # Get the arc-s, and the rest
    LonS = float(LonStr[0])
    Lon = LonD + LonM/60. + LonS/3600.
    if LonStr[1] in ['W', 'w']:
        Lon = -1*Lon
    # Same for Latitude
    LatStr = LatStr.split('d')
    LatD = int(LatStr[0])
    LatStr = LatStr[1].split('\'')
    LatM = int(LatStr[0])
    LatStr = LatStr[1].split('"')
    LatS = float(LatStr[0])
    Lat = LatD + LatM/60. + LatS/3600.
    if LatStr[1] in ['S', 's']:
        Lat = -1*Lat
    return Lat, Lon

FileName = Image.cub
# Mine's an ISIS3 cube file.
CornerLats, CornerLons = GetCornerCoordinates(FileName)
# UpperLeft, LowerLeft, UpperRight, LowerRight, Centre
print CornerLats
print CornerLons

E questo mi dà:

[-4.33429444 -5.82895833 -4.33429444 -5.82895833 -5.081625  ] 
[ 137.12275833  137.12203333  137.74633889  137.74706111  137.43454722]

Risposte:


29

Ecco un altro modo per farlo senza chiamare un programma esterno.

Quello che fa è ottenere le coordinate dei quattro angoli dalla geotrasformazione e riproiettarle in lon / lat usando osr.CoordinateTransformation.

from osgeo import gdal,ogr,osr

def GetExtent(gt,cols,rows):
    ''' Return list of corner coordinates from a geotransform

        @type gt:   C{tuple/list}
        @param gt: geotransform
        @type cols:   C{int}
        @param cols: number of columns in the dataset
        @type rows:   C{int}
        @param rows: number of rows in the dataset
        @rtype:    C{[float,...,float]}
        @return:   coordinates of each corner
    '''
    ext=[]
    xarr=[0,cols]
    yarr=[0,rows]

    for px in xarr:
        for py in yarr:
            x=gt[0]+(px*gt[1])+(py*gt[2])
            y=gt[3]+(px*gt[4])+(py*gt[5])
            ext.append([x,y])
            print x,y
        yarr.reverse()
    return ext

def ReprojectCoords(coords,src_srs,tgt_srs):
    ''' Reproject a list of x,y coordinates.

        @type geom:     C{tuple/list}
        @param geom:    List of [[x,y],...[x,y]] coordinates
        @type src_srs:  C{osr.SpatialReference}
        @param src_srs: OSR SpatialReference object
        @type tgt_srs:  C{osr.SpatialReference}
        @param tgt_srs: OSR SpatialReference object
        @rtype:         C{tuple/list}
        @return:        List of transformed [[x,y],...[x,y]] coordinates
    '''
    trans_coords=[]
    transform = osr.CoordinateTransformation( src_srs, tgt_srs)
    for x,y in coords:
        x,y,z = transform.TransformPoint(x,y)
        trans_coords.append([x,y])
    return trans_coords

raster=r'somerasterfile.tif'
ds=gdal.Open(raster)

gt=ds.GetGeoTransform()
cols = ds.RasterXSize
rows = ds.RasterYSize
ext=GetExtent(gt,cols,rows)

src_srs=osr.SpatialReference()
src_srs.ImportFromWkt(ds.GetProjection())
#tgt_srs=osr.SpatialReference()
#tgt_srs.ImportFromEPSG(4326)
tgt_srs = src_srs.CloneGeogCS()

geo_ext=ReprojectCoords(ext,src_srs,tgt_srs)

Alcuni codice dalla metageta del progetto, osr.CoordinateTransformation idea da questa risposta


Fantastico, grazie. E funziona indipendentemente dall'esistenza delle righe appropriate nell'output di gdalinfo.
EddyTheB,

Penso che sarà meglio con tgt_srs = src_srs.CloneGeogCS (). I miei raster iniziali sono immagini di Marte, quindi l'utilizzo di EPSG (4326) non è l'ideale, ma CloneGeogCS () sembra passare da coordinate proiettate a coordinate geografiche.
EddyTheB,

Di sicuro. Ho aggiornato la risposta per utilizzare CloneGeogCS. Tuttavia, stavo solo cercando di dimostrare l'uso delle funzioni GetExtent e ReprojectCoords. Puoi usare tutto ciò che desideri come target srs.
user2856

Sì, grazie, non avrei mai trovato quelli altrimenti.
EddyTheB,

Cosa succede se si dispone di un set di dati che non ha proiezioni e specifica RPC? L'importazione dalla funzione wkt non riesce. È possibile calcolare l'estensione usando un trasformatore ma mi chiedevo se c'era un modo con il metodo sopra?
Thomas,

41

Questo può essere fatto in molte meno righe di codice

src = gdal.Open(path goes here)
ulx, xres, xskew, uly, yskew, yres  = src.GetGeoTransform()
lrx = ulx + (src.RasterXSize * xres)
lry = uly + (src.RasterYSize * yres)

ulx, ulyÈ l'angolo in alto a sinistra, lrx, lryè l'angolo in basso a destra

La libreria osr (parte di gdal) può essere utilizzata per trasformare i punti in qualsiasi sistema di coordinate. Per un singolo punto:

from osgeo import ogr
from osgeo import osr

# Setup the source projection - you can also import from epsg, proj4...
source = osr.SpatialReference()
source.ImportFromWkt(src.GetProjection())

# The target projection
target = osr.SpatialReference()
target.ImportFromEPSG(4326)

# Create the transform - this can be used repeatedly
transform = osr.CoordinateTransformation(source, target)

# Transform the point. You can also create an ogr geometry and use the more generic `point.Transform()`
transform.TransformPoint(ulx, uly)

Per riproiettare un'immagine raster intero sarebbe una questione molto più complicata, ma GDAL> = 2.0 offre una soluzione facile anche per questo: gdal.Warp.


Questa è la risposta Pythonic per la misura - una soluzione ugualmente Pythonic per la riproiezione sarebbe stata fantastica, Detto questo - Uso i risultati in PostGIS, quindi passo semplicemente l'estensione non trasformata e ST_Transform(ST_SetSRID(ST_MakeBox2D([i risultati] ),28355),4283). (Un cavillo - la "T" in src.GetGeoTransform()deve essere maiuscola).
GT.

@GT. Aggiunto un esempio
James,

1

L'ho fatto in questo modo ... è un po 'codificato ma se non cambia nulla con gdalinfo, funzionerà con le immagini proiettate UTM!

imagefile= /pathto/image
p= subprocess.Popen(["gdalinfo", "%s"%imagefile], stdout=subprocess.PIPE)
out,err= p.communicate()
ul= out[out.find("Upper Left")+15:out.find("Upper Left")+38]
lr= out[out.find("Lower Right")+15:out.find("Lower Right")+38]

2
Questo è abbastanza fragile in quanto si basa sul fatto di gdalinfoessere disponibile sul percorso di un utente (non sempre nel caso, in particolare su Windows) e di analizzare un output stampato che non ha un'interfaccia rigorosa, vale a dire fare affidamento su quei "numeri magici" per una spaziatura corretta. Non è necessario quando gdal fornisce associazioni complete di pitone che possono farlo in un modo più esplicito e robusto
James,

1

Se il tuo raster viene ruotato, la matematica diventa un po 'più complicata, poiché devi considerare ciascuno dei sei coefficienti di trasformazione affine. Prendi in considerazione l'utilizzo di affine per trasformare una trasformazione / geotrasformazione affine ruotata:

from affine import Affine

# E.g., from a GDAL DataSet object:
# gt = ds.GetGeoTransform()
# ncol = ds.RasterXSize
# nrow = ds.RasterYSize

# or to work with a minimal example
gt = (100.0, 17.320508075688775, 5.0, 200.0, 10.0, -8.660254037844387)
ncol = 10
nrow = 15

transform = Affine.from_gdal(*gt)
print(transform)
# | 17.32, 5.00, 100.00|
# | 10.00,-8.66, 200.00|
# | 0.00, 0.00, 1.00|

Ora puoi generare le quattro coppie di coordinate d'angolo:

c0x, c0y = transform.c, transform.f  # upper left
c1x, c1y = transform * (0, nrow)     # lower left
c2x, c2y = transform * (ncol, nrow)  # lower right
c3x, c3y = transform * (ncol, 0)     # upper right

E se hai bisogno anche dei limiti basati sulla griglia (xmin, ymin, xmax, ymax):

xs = (c0x, c1x, c2x, c3x)
ys = (c0y, c1y, c2y, c3y)
bounds = min(xs), min(ys), max(xs), max(ys)

0

Credo che nelle versioni più recenti del modulo OSGEO / GDAL per Python si possano chiamare direttamente le utility GDAL dal codice senza coinvolgere le chiamate di sistema. ad esempio invece di utilizzare il sottoprocesso per chiamare:

gdalinfo si può chiamare gdal.Info (nome_del_file) per avere un'esposizione dei file metadati / annotazione

o invece di usare il sottoprocesso per chiamare: gdalwarp si può chiamare gdal.Warp per fare il warping su un raster.

L'elenco delle utility GDAL attualmente disponibili come funzione interna: http://erouault.blogspot.com/2015/10/gdal-and-ogr-utilities-as-library.html

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.