Scaricare dati raster in Python da Postgis usando psycopg2


13

Ho dati raster in una tabella di Postgres che voglio ottenere in Python come una matrice numpy. Sto usando psycopg2 per connettermi al db. Sono in grado di scaricare i dati ma ritorna come una stringa (probabilmente un binario serializzato).

Qualcuno sa come prendere questa stringa e convertirla in una matrice numpy?

Ho esplorato altre opzioni per scaricare il raster come usare st_astiff e codificare per scaricare il file hex e usare xxd ma non ha funzionato. Continuo a ricevere l'errore "rt_raster_to_gdal: Impossibile caricare il driver GDAL di output" e non ho i permessi per impostare le variabili di ambiente per poter accendere i driver.

TL, DR: desidera importare dati raster in una matrice numpy (usando Python).

Risposte:


14

rt_raster_to_gdal: Impossibile caricare il driver GDAL di output

Per quanto riguarda il primo errore con ST_AsTIFF , è necessario abilitare i driver GDAL, che per impostazione predefinita non sono abilitati per PostGIS 2.1. Vedere il manuale sui modi per fare questo. Ad esempio, ho una variabile di ambiente impostata su un computer Windows con:

POSTGIS_GDAL_ENABLED_DRIVERS=GTiff PNG JPEG GIF XYZ DTED USGSDEM AAIGrid

che può essere confermato con PostGIS con:

SELECT short_name, long_name
FROM ST_GDALDrivers();

PostGIS a Numpy

È possibile esportare l'output in un file GeoTIFF di memoria virtuale affinché GDAL possa leggere in un array Numpy. Per suggerimenti sui file virtuali utilizzati in GDAL, vedere questo post del blog .

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

Mostra un punto bufferizzato rasterizzato.

[[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]]

Si noti che ho usato un formato "GTiff" nell'esempio, ma altri formati potrebbero essere più adatti. Ad esempio, se si dispone di un raster di grandi dimensioni che deve essere trasferito attraverso una connessione Internet lenta, provare a utilizzare "PNG" per comprimerlo.


Questo è molto utile.
John Powell,

Molto utile. Grazie! Sto ancora incontrando questo problema che: ERRORE: rt_raster_to_gdal: Impossibile caricare il driver GDAL di output ma penso di avere una soluzione per questo. grazie ancora!
Mayank Agarwal,

@MayankAgarwal ha aggiornato la risposta per l'errore rt_raster_to_gdal.
Mike T,

6

Penso che la domanda fosse se si può leggere dalle tabelle raster postgis SENZA driver gdal abilitati. Come tutte le cose Python, puoi!

Assicurati di selezionare il risultato raster come WKBinary:

seleziona St_AsBinary (rast) ...

Usa lo script qui sotto per decifrare WKBinary in un formato immagine Python. Preferisco opencv, perché gestisce un numero arbitrario di bande di immagini, ma si può usare PIL / basso se 1 o 3 bande sono più comuni.

Per ora gestisco solo immagini byte, ma è relativamente banale espandersi ad altri tipi di dati.

Spero sia utile.

import struct
importa numpy come np
import cv2

# Funzione per decifrare l'intestazione WKB
def wkbHeader (raw):
    # Vedi http://trac.osgeo.org/postgis/browser/trunk/raster/doc/RFC2-WellKnownBinaryFormat

    intestazione = {}

    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]

    intestazione di ritorno

# Funzione per decifrare i dati raster WKB 
def wkbImage (raw):
    h = wkbHeader (grezzo)
    img = [] # array per memorizzare bande di immagini
    offset = 61 # lunghezza non elaborata dell'intestazione in byte
    per i nell'intervallo (h ['nbands']):
        # Determina il tipo di pixel per questa band
        pixtype = struct.unpack ('B', raw [offset]) [0] >> 4
        # Per ora, gestiamo solo byte senza segno
        se 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 ['larghezza'] * h ['altezza']
        # da fare: gestire altri tipi di dati 

    return cv2.merge (tuple (img))


Questo è molto utile. Ho avuto molti problemi con gdal in un ambiente conda, ma questo approccio ha funzionato per la prima volta ed è bello poter approfondire un po 'anche la struttura.
John Powell,
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.