Ottenere elevazione a lat / long da raster usando Python?


10

Mi chiedevo se qualcuno avesse qualche esperienza nell'ottenere i dati di elevazione da un raster senza usare ArcGIS , ma piuttosto ottenere le informazioni come un pitone listo dict?

Ricevo i miei dati XY come un elenco di tuple:

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

Vorrei scorrere l'elenco o passarlo a una funzione o un metodo di classe per ottenere l'elevazione corrispondente per le coppie xy.

Ho fatto alcune ricerche sull'argomento e l' API di gdal sembra promettente. Qualcuno può darmi consigli su come procedere su cose, insidie, codice di esempio?


GDAL non è un'opzione poiché non riesco a modificare la variabile del percorso di sistema sulla macchina su cui sto lavorando!

Qualcuno sa di un approccio diverso?


2
sfortunatamente, devi davvero far funzionare GDAL sul tuo sistema per fare qualsiasi cosa con un raster in Python. Con "impossibile modificare la variabile del percorso di sistema sul computer", ti riferisci a queste istruzioni ? Trovo questo metodo di installazione molto scarso e non lo uso né lo consiglio. Se stai usando Windows, installa GDAL / Python in modo semplice .
Mike T,

Sì, lo ero davvero. Non sono al lavoro in questo momento, ma controllerò il link che hai pubblicato. Sembra promettente! Grazie per essere tornato alla mia domanda!
LarsVegas,

Ho usato il programma di installazione di Christoph Gohlke (linkato sopra) su molti computer di lavoro, ed è davvero semplice. Devi solo assicurarti di abbinare la versione di Python e Windows a 32 o 64 bit. Mentre ci sei, dovresti anche ottenere NumPy dallo stesso posto, dal momento che è necessario da GDAL, come mostrato nelle risposte di seguito.
Mike T,

Risposte:


15

Ecco un modo più programmatico di utilizzare GDAL rispetto alla risposta di @Aragon. Non l'ho testato, ma è principalmente il codice della piastra della caldaia che ha funzionato per me in passato. Si basa su attacchi Numpy e GDAL, ma questo è tutto.

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

1
vedi la modifica alla mia domanda ... grazie comunque per la pubblicazione! L'ho votato.
LarsVegas,

1
Ah dannazione! Beh, almeno è qui per i posteri. TBH, la matematica mapToPixel()e pixelToMap()sono la parte importante, purché sia ​​possibile creare una matrice intorpidita (o una normale Python, ma generalmente non sono così efficienti per questo tipo di cose) e ottenere il riquadro di delimitazione geografica della matrice.
MerseyViking,

1
+1 per il commento (e il codice) sull'inversione dei parametri nell'array numpy. Stavo cercando ovunque un bug nel mio codice e questo scambio lo ha risolto!
aldo,

1
Quindi suggerisco che la tua matrice ( gtnell'esempio) sia sbagliata. Una matrice affine come utilizzata in CGAL (vedi: gdal.org/gdal_datamodel.html ) è generalmente invertibile (altrimenti hai alcuni valori di ridimensionamento funky in corso). Quindi dove possiamo g = p.Afare anche p = g.A^-1Numpy.linalg è un po 'pesante per i nostri scopi: possiamo ridurre tutto a due semplici equazioni.
MerseyViking,

1
Ho modificato nuovamente il codice per utilizzare l'algebra semplice anziché il linalg intorpidito. Se la matematica è sbagliata, correggi la pagina di Wikipedia.
MerseyViking l'

3

Controlla la mia risposta qui ... e leggi qui per alcune informazioni. Le seguenti informazioni sono state prese da Geotips:

Con gdallocationinfo , possiamo interrogare l'elevazione in un punto:

$ gdallocationinfo gmted/all075.vrt -geoloc 87360 19679

L'output del comando sopra ha la forma:

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

Ciò significa che il valore di elevazione nella geolocalizzazione fornita è 1418.


Ho appena scoperto che non posso usare GDAL in quanto non sono in grado di modificare la mia variabile di sistema sulla macchina su cui sto lavorando. Grazie per l'input però.
LarsVegas,

0

Vedi ad esempio questo codice basato su GDAL (e Python, non è necessario il numpy): https://github.com/geometalab/retrieve-height-service


È un peccato che il codice non sembra essere una licenza open source.
Ben Crowell,

Ora ha :-).
Stefan,

-1

Il codice python fornito estrae i dati di valore di una cella raster in base ai dati x, y forniti. È una versione leggermente alterata di un esempio di questa eccellente fonte . Si basa su GDALe numpyche non fanno parte della distribuzione standard di Python. Grazie a @Mike Toews per aver segnalato i binari di Windows non ufficiali per i pacchetti di estensione Python per rendere l'installazione e l'uso facili e veloci.

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

Sembra che questa sia solo una versione meno generica e meno flessibile di ciò che MerseyViking ha offerto sopra?
WileyB,
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.