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.