Jak uzyskać współrzędne rastra za pomocą powiązań Python GDAL?

30

Czy istnieje sposób na uzyskanie współrzędnych narożnych (w stopniach lat / long) z pliku rastrowego za pomocą powiązań Pythona gdal?

Kilka wyszukiwań w Internecie przekonało mnie, że tak nie jest, więc opracowałem rozwiązanie, analizując dane wyjściowe gdalinfo, jest to nieco podstawowe, ale pomyślałem, że może zaoszczędzić trochę czasu dla ludzi, którzy mogą czuć się mniej komfortowo z pythonem. Działa również tylko wtedy, gdy gdalinfo zawiera współrzędne geograficzne wraz ze współrzędnymi narożnymi, co nie wydaje mi się, że zawsze tak jest.

Oto moje obejście, czy ktoś ma jakieś lepsze rozwiązania?

gdalinfo na odpowiednim rastrze daje wynik podobny do tego w połowie wyniku:

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)

Ten kod będzie działał na plikach, które tak wyglądają gdalinfo. Wierzę, że czasami współrzędne będą podawane w stopniach i dziesiętnych, a nie w stopniach, minutach i sekundach; dostosowanie kodu do takiej sytuacji powinno być trywialne.

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

A to daje mi:

[-4.33429444 -5.82895833 -4.33429444 -5.82895833 -5.081625  ] 
[ 137.12275833  137.12203333  137.74633889  137.74706111  137.43454722]
EddyTheB
źródło
Być może powiązane: gis.stackexchange.com/questions/33330/…
AndreJ

Odpowiedzi:

29

Oto inny sposób, aby to zrobić bez wywoływania programu zewnętrznego.

W ten sposób uzyskuje się współrzędne czterech rogów z geotransformy i ponownie rzutuje je na długość / długość za pomocą 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)

Niektóre kod z metageta projektu osr.CoordinateTransformation pomysł z tą odpowiedź

użytkownik2856
źródło
Wielkie dzieki. I działa niezależnie od tego, czy w wyjściu gdalinfo istnieją odpowiednie linie.
EddyTheB 16.04.13
Myślę, że tak będzie lepiej z tgt_srs = src_srs.CloneGeogCS (). Moje początkowe rastry to obrazy Marsa, więc użycie EPSG (4326) nie jest idealne, ale wydaje się, że CloneGeogCS () po prostu zmienia współrzędne rzutowane na współrzędne geograficzne.
EddyTheB
Na pewno. Zaktualizowałem odpowiedź, aby użyć CloneGeogCS. Próbowałem jednak tylko zademonstrować użycie funkcji GetExtent i ReprojectCoords. Możesz użyć czegokolwiek, co chcesz, jako docelowych srs.
user2856 19.04.13
Tak, dziękuję, nigdy bym ich nie znalazł.
EddyTheB
Co zrobić, jeśli masz zestaw danych, który nie ma projekcji i określa RPC? Import z funkcji wkt kończy się niepowodzeniem. Można obliczyć zasięg za pomocą transformatora, ale zastanawiałem się, czy istnieje sposób z powyższą metodą?
Thomas
41

Można to zrobić w znacznie mniejszej liczbie wierszy kodu

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, ulyto lewy górny róg lrx, lryto prawy dolny róg

Biblioteki osr (część gdal) można użyć do przekształcenia punktów w dowolny układ współrzędnych. Dla jednego punktu:

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)

Aby odwzorować cały obraz rastrowy będzie o wiele bardziej skomplikowana sprawa, ale GDAL> = 2.0 oferuje proste rozwiązanie dla tego też: gdal.Warp.

James
źródło
To jest odpowiedź Pythona na ten zakres - równie Pythońskie rozwiązanie do ponownej odrzucenia byłoby niesamowite. Powiedziałem, że - używam wyników w PostGIS, więc po prostu przekazuję nieprzetworzony zakres i ST_Transform(ST_SetSRID(ST_MakeBox2D([wyniki] ),28355),4283)to. (Jeden spór - litera „T” src.GetGeoTransform()powinna być pisana wielkimi literami).
GT.
@GT. Dodano przykład
James
1

Zrobiłem w ten sposób ... jest trochę zakodowany, ale jeśli nic nie zmieni się z gdalinfo, będzie działać dla obrazów wyświetlanych w 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]
Emiliano
źródło
2
Jest to dość kruche, ponieważ polega zarówno na gdalinfobyciu dostępnym na ścieżce użytkownika (nie zawsze tak jest, szczególnie w systemie Windows), jak i na analizowaniu wydruków, które nie mają ścisłego interfejsu - tj. Polegając na tych „magicznych liczbach” dla poprawnego odstępu. Nie jest konieczne, gdy gdal zapewnia kompleksowe wiązania Pythona, które mogą to zrobić w bardziej wyraźny i niezawodny sposób
James
1

Jeśli twój raster jest obrócony, matematyka staje się nieco bardziej skomplikowana, ponieważ musisz wziąć pod uwagę każdy z sześciu współczynników transformacji afinicznej. Rozważ użycie afinii do przekształcenia obróconej transformacji afinicznej / geotransform:

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|

Teraz możesz wygenerować cztery pary współrzędnych narożnych:

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

A jeśli potrzebujesz również granic opartych na siatce (xmin, ymin, xmax, ymax):

xs = (c0x, c1x, c2x, c3x)
ys = (c0y, c1y, c2y, c3y)
bounds = min(xs), min(ys), max(xs), max(ys)
Mike T.
źródło
0

Wierzę, że w nowszych wersjach modułu OSGEO / GDAL dla pytona można bezpośrednio wywoływać narzędzia GDAL z kodu bez angażowania wywołań systemowych. na przykład zamiast używać podprocesu do wywoływania:

gdalinfo można wywołać gdal.Info (nazwa_pliku), aby uzyskać dostęp do metadanych / adnotacji pliku

lub zamiast używać podprocesu do wywoływania: gdalwarp, możesz cal gdal.Warp zrobić wypaczenie na rastrze.

Lista narzędzi GDAL dostępnych obecnie jako funkcja wewnętrzna: http://erouault.blogspot.com/2015/10/gdal-and-ogr-utilities-as-library.html

Sinooshka
źródło