Herunterladen von Rasterdaten mit psycopg2 aus Postgis in Python

13

Ich habe Rasterdaten in einer Postgres-Tabelle, die ich als Numpy-Array in Python aufnehmen möchte. Ich benutze psycopg2, um eine Verbindung zur Datenbank herzustellen. Ich kann die Daten herunterladen, sie werden jedoch als Zeichenfolge zurückgegeben (wahrscheinlich als serialisierte Binärdatei).

Weiß jemand, wie man diesen String nimmt und in ein Numpy-Array konvertiert?

Ich habe andere Optionen zum Herunterladen des Rasters untersucht, z. B. "st_astiff" und "encode", um die Hex-Datei herunterzuladen und "xxd" zu verwenden, aber das hat nicht funktioniert. Ich erhalte immer wieder den Fehler 'rt_raster_to_gdal: Der GDAL-Ausgangstreiber konnte nicht geladen werden' und ich habe keine Berechtigung zum Festlegen der Umgebungsvariablen, um die Treiber einschalten zu können.

TL, DR: Sie möchten Rasterdaten in ein NumPy-Array importieren (mit Python).

Mayank Agarwal
quelle

Antworten:

14

rt_raster_to_gdal: Der GDAL-Ausgangstreiber konnte nicht geladen werden

Wie für den ersten Fehler mit ST_AsTIFF müssen Sie Ihre GDAL-Treiber aktivieren, die standardmäßig nicht für PostGIS 2.1 aktiviert sind. Informationen dazu finden Sie im Handbuch . Zum Beispiel habe ich eine Umgebungsvariable auf einem Windows-Computer eingerichtet mit:

POSTGIS_GDAL_ENABLED_DRIVERS=GTiff PNG JPEG GIF XYZ DTED USGSDEM AAIGrid

was mit PostGIS bestätigt werden kann mit:

SELECT short_name, long_name
FROM ST_GDALDrivers();

PostGIS nach Numpy

Sie können die Ausgabe in eine GeoTIFF-Datei des virtuellen Speichers exportieren, damit GDAL sie in ein Numpy-Array einliest. Hinweise zu in GDAL verwendeten virtuellen Dateien finden Sie in diesem Blogbeitrag .

import os
import psycopg2
from osgeo import gdal

# Adjust this to connect to a PostGIS database
conn = psycopg2.connect(...)
curs = conn.cursor()

# Make a dummy table with raster data
curs.execute("""\
    SELECT ST_AsRaster(ST_Buffer(ST_Point(1, 5), 10), 10, 10, '8BUI', 1) AS rast
    INTO TEMP mytable;
""")

# Use a virtual memory file, which is named like this
vsipath = '/vsimem/from_postgis'

# Download raster data into Python as GeoTIFF, and make a virtual file for GDAL
curs.execute("SELECT ST_AsGDALRaster(rast, 'GTiff') FROM mytable;")
gdal.FileFromMemBuffer(vsipath, bytes(curs.fetchone()[0]))

# Read first band of raster with GDAL
ds = gdal.Open(vsipath)
band = ds.GetRasterBand(1)
arr = band.ReadAsArray()

# Close and clean up virtual memory file
ds = band = None
gdal.Unlink(vsipath)

print(arr)  # this is a 2D numpy array

Zeigt einen gerasterten gepufferten Punkt an.

[[0 0 0 1 1 1 1 0 0 0]
 [0 1 1 1 1 1 1 1 1 0]
 [0 1 1 1 1 1 1 1 1 0]
 [1 1 1 1 1 1 1 1 1 1]
 [1 1 1 1 1 1 1 1 1 1]
 [1 1 1 1 1 1 1 1 1 1]
 [1 1 1 1 1 1 1 1 1 1]
 [0 1 1 1 1 1 1 1 1 0]
 [0 1 1 1 1 1 1 1 1 0]
 [0 0 0 1 1 1 1 0 0 0]]

Beachten Sie, dass ich in diesem Beispiel ein GTiff-Format verwendet habe, andere Formate jedoch möglicherweise besser geeignet sind. Wenn Sie beispielsweise ein großes Raster haben, das über eine langsame Internetverbindung übertragen werden muss, versuchen Sie, es mit 'PNG' zu komprimieren.

Mike T
quelle
Das ist sehr hilfreich.
John Powell
Sehr hilfreich. Vielen Dank! Ich stoße immer noch auf das folgende Problem: FEHLER: rt_raster_to_gdal: Der GDAL-Ausgabetreiber konnte nicht geladen werden, aber ich glaube, ich habe eine Problemumgehung dafür. Danke noch einmal!
Mayank Agarwal
@MayankAgarwal hat die Antwort auf den Fehler rt_raster_to_gdal aktualisiert.
Mike T
6

Ich denke die Frage war, ob man aus postgis Rastertabellen OHNE gdal Treiber lesen kann. Wie alles in Python können Sie!

Stellen Sie sicher, dass Sie Ihr Raster-Ergebnis als WKBinary auswählen:

wähle St_AsBinary (rast) ...

Verwenden Sie das folgende Skript, um WKBinary in ein Python-Image-Format zu entschlüsseln. Ich bevorzuge opencv, weil es eine beliebige Anzahl von Bildbändern verarbeitet, aber man kann PIL / low verwenden, wenn 1 oder 3 Bänder üblicher sind.

Im Moment beschäftige ich mich nur mit Byte-Bildern, aber es ist relativ trivial, sie auf andere Datentypen zu erweitern.

Hoffe das ist nützlich.

Struktur importieren
numpy als np importieren
cv2 importieren

# Funktion zum Entschlüsseln des WKB-Headers
def wkbHeader (raw):
    # Siehe http://trac.osgeo.org/postgis/browser/trunk/raster/doc/RFC2-WellKnownBinaryFormat

    header = {}

    header ['endianess'] = struct.unpack ('B', raw [0]) [0]
    header ['version'] = struct.unpack ('H', raw [1: 3]) [0]
    header ['nbands'] = struct.unpack ('H', raw [3: 5]) [0]
    header ['scaleX'] = struct.unpack ('d', raw [5:13]) [0]
    header ['scaleY'] = struct.unpack ('d', raw [13:21]) [0]
    header ['ipX'] = struct.unpack ('d', raw [21:29]) [0]
    header ['ipY'] = struct.unpack ('d', raw [29:37]) [0]
    header ['skewX'] = struct.unpack ('d', raw [37:45]) [0]
    header ['skewY'] = struct.unpack ('d', raw [45:53]) [0]
    header ['srid'] = struct.unpack ('i', raw [53:57]) [0]
    header ['width'] = struct.unpack ('H', raw [57:59]) [0]
    header ['height'] = struct.unpack ('H', raw [59:61]) [0]

    Kopfzeile zurückgeben

# Funktion zum Entschlüsseln der WKB-Rasterdaten 
def wkbImage (raw):
    h = wkbHeader (roh)
    img = [] # Array zum Speichern von Bildbändern
    offset = 61 # Header-Raw-Länge in Bytes
    für i in range (h ['nbands']):
        # Bestimmen Sie den Pix-Typ für dieses Band
        pixtype = struct.unpack ('B', raw [offset]) [0] >> 4
        # Derzeit verarbeiten wir nur vorzeichenlose Bytes
        wenn pixtype == 4:
            band = np.frombuffer (raw, dtype = 'uint8', count = h ['width'] * h ['height'], offset = offset + 1)
            img.append ((np.reshape (band, ((h ['height'], h ['width']))))
            Offset = Offset + 2 + h ['Breite'] * h ['Höhe']
        # zu tun: andere Datentypen behandeln 

    return cv2.merge (tuple (img))

GGL
quelle
Das ist sehr hilfreich. Ich hatte viele Probleme mit gdal in einer Conda-Umgebung, aber dieser Ansatz hat zum ersten Mal funktioniert, und es ist schön, ein bisschen in die Struktur eintauchen zu können.
John Powell