Uzyskiwanie wysokości na poziomie lat / long z rastra za pomocą pytona?

10

Zastanawiałem się, czy ktoś ma jakieś doświadczenie w uzyskiwaniu danych wysokościowych z rastra bez korzystania z ArcGIS , ale raczej otrzymuje te informacje jako python listlub dict?

Otrzymuję moje dane XY jako listę krotek:

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

Chciałbym zapętlić listę lub przekazać ją do funkcji lub metody klasy, aby uzyskać odpowiednie rzędne dla par xy.

Przeprowadziłem badania na ten temat, a gdal API brzmi obiecująco. Czy ktoś może mi doradzić, jak poradzić sobie z problemami, pułapkami, przykładowym kodem?


GDAL nie jest opcją, ponieważ nie mogę edytować zmiennej ścieżki systemowej na komputerze, nad którym pracuję!

Czy ktoś wie o innym podejściu?

LarsVegas
źródło
2
niestety, naprawdę musisz uruchomić GDAL w swoim systemie, aby zrobić cokolwiek z rastrem w Pythonie. Czy „nie możesz edytować zmiennej ścieżki systemowej na komputerze”, czy odwołujesz się do tych instrukcji ? Uważam tę metodę instalacji za bardzo kiepską i nie używam jej ani nie zalecam. Jeśli używasz systemu Windows, zainstaluj GDAL / Python w prosty sposób .
Mike T
Tak, rzeczywiście byłem. Nie jestem teraz w pracy, ale sprawdzę link, który opublikowałeś. Wygląda obiecująco! Dzięki, że wróciłeś do mojego pytania!
LarsVegas,
Użyłem instalatora autorstwa Christopha Gohlke (link powyżej) na wielu komputerach roboczych i jest to naprawdę proste. Musisz tylko upewnić się, że pasujesz do wersji Pythona i 32- lub 64-bitowego systemu Windows. W tym czasie powinieneś również zdobyć NumPy z tego samego miejsca, ponieważ jest to potrzebne GDAL, jak pokazano w odpowiedziach poniżej.
Mike T

Odpowiedzi:

15

Oto bardziej programowy sposób użycia GDAL niż odpowiedź @ Aragon. Nie testowałem tego, ale w przeszłości działał dla mnie głównie kod z płyty kotłowej. Opiera się na powiązaniach Numpy i GDAL, ale o to chodzi.

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
źródło
1
zobacz edycję mojego pytania ... mimo to dziękuję za opublikowanie! Głosowałem za tym.
LarsVegas,
1
Ach cholera! Przynajmniej jest tutaj dla potomności. TBH, matematyka jest mapToPixel()i pixelToMap()jest ważnym bitem, o ile możesz stworzyć tablicę numpy (lub zwykłą tablicę Pythona, ale na ogół nie są one tak wydajne dla tego rodzaju rzeczy) i uzyskać obwiednię geograficzną tablicy.
MerseyViking
1
+1 za komentarz (i kod) o odwróceniu parametrów do tablicy numpy. Wszędzie szukałem błędu w moim kodzie, a ta zamiana go naprawiła!
aldo
1
Następnie sugeruję, że twoja matryca ( gtw tym przykładzie) jest błędna. Macierz afiniczna stosowana w CGAL (patrz: gdal.org/gdal_datamodel.html ) jest na ogół odwracalna (w przeciwnym razie masz trochę funky skalowania). Więc tam, gdzie mamy, g = p.Amożemy również wykonać p = g.A^-1Numpy.linalg jest nieco ciężki dla naszych celów - możemy sprowadzić wszystko do dwóch prostych równań.
MerseyViking,
1
Zmodyfikowałem kod, aby użyć prostej algebry zamiast numpy linalg. Jeśli matematyka jest nieprawidłowa, napraw stronę Wikipedii.
MerseyViking,
3

Sprawdź moją odpowiedź tutaj ... i przeczytaj tutaj, aby uzyskać informacje. Poniższe informacje pochodzą z Geotips:

Dzięki gdallocationinfo możemy zapytać o wysokość w jednym punkcie:

$ gdallocationinfo gmted/all075.vrt -geoloc 87360 19679

Dane wyjściowe powyższego polecenia mają postać:

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

Oznacza to, że wartość wysokości w podanej geolokalizacji wynosi 1418.

Aragonia
źródło
Właśnie się dowiedziałem, że nie mogę używać GDAL, ponieważ nie jestem w stanie edytować mojej zmiennej systemowej na komputerze, na którym pracuję. W każdym razie dziękuję za wkład.
LarsVegas,
0

Zobacz np. Ten kod oparty na GDAL (i Python, nie jest potrzebny numpy): https://github.com/geometalab/retrieve-height-service

Stefan
źródło
Szkoda, że ​​kod nie wydaje się być licencją typu open source.
Ben Crowell,
Teraz ma :-).
Stefan
-1

Podany kod python wyodrębnia dane wartości komórki rastrowej na podstawie podanych współrzędnych x, y. Jest to nieco zmieniona wersja przykładu z tego doskonałego źródła . Opiera się na standardowej dystrybucji języka Python GDALi numpynie stanowi jej części. Dzięki @Mike Toews za wskazanie nieoficjalnych plików binarnych Windows dla pakietów rozszerzeń Python, aby instalacja i użytkowanie były szybkie i łatwe.

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
źródło
Wygląda na to, że jest to po prostu mniej ogólna, mniej elastyczna wersja tego, co MerseyViking oferuje powyżej?
WileyB,