Erhalten Sie eine Höhe von Lat / Long aus dem Raster mit Python?

10

Ich habe mich gefragt, ob jemand Erfahrung darin hat, Höhendaten aus einem Raster abzurufen , ohne ArcGIS zu verwenden , sondern die Informationen als Python zu erhalten, listoder dict?

Ich erhalte meine XY-Daten als Liste von Tupeln:

xy34 =[perp_obj[j].CalcPnts(float(i.dist), orientation) for j in range (len(perp_obj))]

Ich möchte die Liste durchlaufen oder an eine Funktion oder Klassenmethode übergeben, um die entsprechende Höhe für die xy-Paare zu erhalten.

Ich habe einige Nachforschungen zu diesem Thema angestellt und die gdal- API klingt vielversprechend. Kann mir jemand raten, wie man Dinge, Fallstricke und Beispielcode angeht?


GDAL ist keine Option, da ich die Systempfadvariable auf dem Computer, an dem ich arbeite, nicht bearbeiten kann!

Kennt jemand einen anderen Ansatz?

LarsVegas
quelle
2
Leider müssen Sie GDAL wirklich auf Ihrem System zum Laufen bringen, um mit einem Raster in Python etwas zu tun. Beziehen Sie sich mit "Systempfadvariable kann auf dem Computer nicht bearbeitet werden" auf diese Anweisungen ? Ich finde diese Installationsmethode sehr schlecht und verwende sie weder noch empfehle ich sie. Wenn Sie Windows verwenden, installieren Sie GDAL / Python auf einfache Weise .
Mike T
Ja, das war ich wirklich. Ich bin gerade nicht auf der Arbeit, aber ich werde den Link überprüfen, den Sie gepostet haben. Sieht vielversprechend aus! Vielen Dank, dass Sie auf meine Frage zurückgekommen sind!
LarsVegas
Ich habe das Installationsprogramm von Christoph Gohlke (oben verlinkt) auf vielen Arbeitscomputern verwendet, und es ist wirklich einfach. Sie müssen nur sicherstellen, dass Sie mit der Version von Python und entweder 32- oder 64-Bit-Windows übereinstimmen. Während Sie gerade dabei sind, sollten Sie NumPy auch von derselben Stelle erhalten, da dies von GDAL benötigt wird, wie in den folgenden Antworten gezeigt.
Mike T

Antworten:

15

Hier ist eine programmatischere Art der Verwendung von GDAL als die Antwort von @ Aragon. Ich habe es nicht getestet, aber es ist meistens der Kesselplattencode, der in der Vergangenheit für mich funktioniert hat. Es basiert auf Numpy- und GDAL-Bindungen, aber das war es auch schon.

import osgeo.gdal as gdal
import osgeo.osr as osr
import numpy as np
from numpy import ma

def maFromGDAL(filename):
    dataset = gdal.Open(filename, gdal.GA_ReadOnly)

    if dataset is None:
        raise Exception()

    # Get the georeferencing metadata.
    # We don't need to know the CRS unless we want to specify coordinates
    # in a different CRS.
    #projection = dataset.GetProjection()
    geotransform = dataset.GetGeoTransform()

    # We need to know the geographic bounds and resolution of our dataset.
    if geotransform is None:
        dataset = None
        raise Exception()

    # Get the first band.
    band = dataset.GetRasterBand(1)
    # We need to nodata value for our MaskedArray later.
    nodata = band.GetNoDataValue()
    # Load the entire dataset into one numpy array.
    image = band.ReadAsArray(0, 0, band.XSize, band.YSize)
    # Close the dataset.
    dataset = None

    # Create a numpy MaskedArray from our regular numpy array.
    # If we want to be really clever, we could subclass MaskedArray to hold
    # our georeference metadata as well.
    # see here: http://docs.scipy.org/doc/numpy/user/basics.subclassing.html
    # For details.
    masked_image = ma.masked_values(image, nodata, copy=False)
    masked_image.fill_value = nodata

    return masked_image, geotransform

def pixelToMap(gt, pos):
    return (gt[0] + pos[0] * gt[1] + pos[1] * gt[2],
            gt[3] + pos[0] * gt[4] + pos[1] * gt[5])

# Reverses the operation of pixelToMap(), according to:
# https://en.wikipedia.org/wiki/World_file because GDAL's Affine GeoTransform
# uses the same values in the same order as an ESRI world file.
# See: http://www.gdal.org/gdal_datamodel.html
def mapToPixel(gt, pos):
    s = gt[0] * gt[4] - gt[3] * gt[1]
    x = (gt[4] * pos[0] - gt[1] * pos[1] + gt[1] * gt[5] - gt[4] * gt[2]) / s
    y = (-gt[3] * pos[0] + gt[0] * pos[1] + gt[3] * gt[2] - gt[0] * gt[5]) / s
    return (x, y)

def valueAtMapPos(image, gt, pos):
    pp = mapToPixel(gt, pos)
    x = int(pp[0])
    y = int(pp[1])

    if x < 0 or y < 0 or x >= image.shape[1] or y >= image.shape[0]:
        raise Exception()

    # Note how we reference the y column first. This is the way numpy arrays
    # work by default. But GDAL assumes x first.
    return image[y, x]

try:
    image, geotransform = maFromGDAL('myimage.tif')
    val = valueAtMapPos(image, geotransform, (434323.0, 2984745.0))
    print val
except:
    print('Something went wrong.')
MerseyViking
quelle
1
siehe die Bearbeitung meiner Frage ... danke fürs posten trotzdem! Ich habe es positiv bewertet.
LarsVegas
1
Ah verdammt! Zumindest ist es hier für die Nachwelt. TBH, die Mathematik in mapToPixel()und pixelToMap()sind das wichtige Bit, solange Sie ein numpy-Array (oder ein reguläres Python-Array, aber im Allgemeinen nicht so effizient für solche Dinge) erstellen und den geografischen Begrenzungsrahmen des Arrays erhalten können.
MerseyViking
1
+1 für den Kommentar (und den Code) zum Umkehren der Parameter in das Numpy-Array. Ich habe überall nach einem Fehler in meinem Code gesucht und dieser Tausch hat ihn behoben!
Aldo
1
Dann schlage ich vor, dass Ihre Matrix ( gtim Beispiel) falsch ist. Eine affine Matrix, wie sie in CGAL verwendet wird (siehe: gdal.org/gdal_datamodel.html ), ist im Allgemeinen invertierbar (andernfalls werden einige funky Skalierungswerte ausgeführt). Wo wir also haben g = p.A, können wir auch p = g.A^-1Numpy machen. Linalg ist für unsere Zwecke ein bisschen schwergewichtig - wir können alles auf zwei einfache Gleichungen reduzieren.
MerseyViking
1
Ich habe den Code überarbeitet, um einfache Algebra anstelle von numpy linalg zu verwenden. Wenn die Mathematik falsch ist, korrigieren Sie die Wikipedia-Seite.
MerseyViking
3

Schauen Sie sich meine Antwort hier an ... und lesen Sie hier einige Informationen. Die folgenden Informationen stammen von Geotips:

Mit gdallocationinfo können wir die Höhe an einem Punkt abfragen:

$ gdallocationinfo gmted/all075.vrt -geoloc 87360 19679

Die Ausgabe des obigen Befehls hat die Form:

Report:
   Location: (87360P,19679L)
Band 1:
   Value: 1418

Dies bedeutet, dass der Höhenwert an der angegebenen Geolokalisierung 1418 beträgt.

Aragon
quelle
Ich habe gerade herausgefunden, dass ich GDAL nicht verwenden kann, da ich meine Systemvariable auf dem Computer, auf dem ich arbeite, nicht bearbeiten kann. Vielen Dank für die Eingabe.
LarsVegas
0

Siehe z. B. diesen Code, der auf GDAL basiert (und Python, keine Nummer erforderlich): https://github.com/geometalab/retrieve-height-service

Stefan
quelle
Es ist bedauerlich, dass der Code nicht Open Source-lizenziert zu sein scheint.
Ben Crowell
Jetzt hat es :-).
Stefan
-1

Der bereitgestellte Python-Code extrahiert die Wertdaten einer Rasterzelle basierend auf gegebenen x, y-Koordinaten. Es ist eine leicht veränderte Version eines Beispiels aus dieser hervorragenden Quelle . Es basiert auf GDALund numpyist nicht Teil der Standard-Python-Distribution. Vielen Dank an @Mike Toews für den Hinweis auf die inoffiziellen Windows-Binärdateien für Python-Erweiterungspakete , um die Installation und Verwendung schnell und einfach zu gestalten.

import os, sys, time, gdal
from gdalconst import *


# coordinates to get pixel values for
xValues = [122588.008]
yValues = [484475.146]

# set directory
os.chdir(r'D:\\temp\\AHN2_060')

# register all of the drivers
gdal.AllRegister()
# open the image
ds = gdal.Open('i25gn1_131.img', GA_ReadOnly)

if ds is None:
    print 'Could not open image'
    sys.exit(1)

# get image size
rows = ds.RasterYSize
cols = ds.RasterXSize
bands = ds.RasterCount

# get georeference info
transform = ds.GetGeoTransform()
xOrigin = transform[0]
yOrigin = transform[3]
pixelWidth = transform[1]
pixelHeight = transform[5]

# loop through the coordinates
for xValue,yValue in zip(xValues,yValues):
    # get x,y
    x = xValue
    y = yValue

    # compute pixel offset
    xOffset = int((x - xOrigin) / pixelWidth)
    yOffset = int((y - yOrigin) / pixelHeight)
    # create a string to print out
    s = "%s %s %s %s " % (x, y, xOffset, yOffset)

    # loop through the bands
    for i in xrange(1,bands):
        band = ds.GetRasterBand(i) # 1-based index
        # read data and add the value to the string
        data = band.ReadAsArray(xOffset, yOffset, 1, 1)
        value = data[0,0]
        s = "%s%s " % (s, value) 
    # print out the data string
    print s
    # figure out how long the script took to run
LarsVegas
quelle
Es scheint, dass dies nur eine weniger generische, weniger flexible Version dessen ist, was MerseyViking oben angeboten hat?
WileyB