Scrittura di array numpy su file raster


30

Sono nuovo di GIS.

Ho un po 'di codice che converte le immagini a infrarossi di Marte in mappe di inerzia termica, che vengono quindi memorizzate come matrici numpy 2D. Ho salvato queste mappe come file hdf5 ma mi piacerebbe davvero salvarle come immagini raster in modo da poterle elaborare in QGIS. Ho fatto diverse ricerche per scoprire come farlo ma senza fortuna. Ho provato a seguire le istruzioni del tutorial su http://www.gis.usu.edu/~chrisg/python/ ma i file che produco usando il suo codice di esempio si aprono come semplici caselle grigie quando li importa in QGIS. Mi sento come se qualcuno potesse suggerire la procedura più semplice possibile a un esempio semplificato di ciò che mi piacerebbe fare, allora potrei essere in grado di fare qualche progresso. Ho QGIS e GDAL, sarei molto felice di installare altri framework che chiunque potrebbe consigliare. Uso Mac OS 10.7.

Quindi, se per esempio ho una matrice insensibile di inerzia termica che assomiglia a:

TI = ( (0.1, 0.2, 0.3, 0.4),
       (0.2, 0.3, 0.4, 0.5),
       (0.3, 0.4, 0.5, 0.6),
       (0.4, 0.5, 0.6, 0.7) )

E per ogni pixel ho la latitudine e la longitudine:

lat = ( (10.0, 10.0, 10.0, 10.0),
        ( 9.5,  9.5,  9.5,  9.5),
        ( 9.0,  9.0,  9.0,  9.0),
        ( 8.5,  8.5,  8.5,  8.5) )
lon = ( (20.0, 20.5, 21.0, 21.5),
        (20.0, 20.5, 21.0, 21.5),
        (20.0, 20.5, 21.0, 21.5),
        (20.0, 20.5, 21.0, 21.5) ) 

Quale procedura consigliano le persone di convertire questi dati in un file raster che posso aprire in QGIS?


A quale diapositiva del tutorial ti riferisci?
RK,

Risposte:


23

Una possibile soluzione al tuo problema: convertilo in un raster ASCII, la cui documentazione è disponibile qui . Questo dovrebbe essere abbastanza facile da fare con Python.

Quindi, con i tuoi dati di esempio sopra, finiresti con il seguente in un file .asc:

ncols 4
nrows 4
xllcorner 20
yllcorner 8.5
cellsize 0.5
nodata_value -9999
0.1 0.2 0.3 0.4
0.2 0.3 0.4 0.5
0.3 0.4 0.5 0.6
0.4 0.5 0.6 0.7

Ciò si aggiunge con successo sia a QGIS che ad ArcGIS, e stilizzato in ArcGIS è simile al seguente: versione raster di cui sopra

Addendum: Sebbene sia possibile aggiungerlo a QGIS come indicato, se si tenta di accedere alle proprietà (per stilizzarlo), QGIS 1.8.0 si blocca. Sto per segnalarlo come un bug. Se questo succede anche a te, allora ci sono molti altri GIS gratuiti là fuori.


È fantastico, grazie. E immagino che dopo aver scritto il mio array come file ASCII potrei convertirlo in un formato binario usando una funzione di conversione pre-scritta.
EddyTheB,

Cordiali saluti, non ho avuto il problema impiccagione con QGIS, ho anche la versione 1.8.0.
EddyTheB,

31

Di seguito è riportato un esempio che ho scritto per un seminario che utilizza i moduli Python numpy e gdal. Legge i dati da un file .tif in un array numpy, esegue una riclassificazione dei valori nell'array e li riscrive in un .tif.

Dalla tua spiegazione, sembra che potresti essere riuscito a scrivere un file valido, ma devi solo simbolizzarlo in QGIS. Se ricordo bene, quando aggiungi un raster per la prima volta, spesso mostra tutto un colore se non hai una mappa dei colori preesistente.

import numpy, sys
from osgeo import gdal
from osgeo.gdalconst import *


# register all of the GDAL drivers
gdal.AllRegister()

# open the image
inDs = gdal.Open("c:/workshop/examples/raster_reclass/data/cropland_40.tif")
if inDs is None:
  print 'Could not open image file'
  sys.exit(1)

# read in the crop data and get info about it
band1 = inDs.GetRasterBand(1)
rows = inDs.RasterYSize
cols = inDs.RasterXSize

cropData = band1.ReadAsArray(0,0,cols,rows)

listAg = [1,5,6,22,23,24,41,42,28,37]
listNotAg = [111,195,141,181,121,122,190,62]

# create the output image
driver = inDs.GetDriver()
#print driver
outDs = driver.Create("c:/workshop/examples/raster_reclass/output/reclass_40.tif", cols, rows, 1, GDT_Int32)
if outDs is None:
    print 'Could not create reclass_40.tif'
    sys.exit(1)

outBand = outDs.GetRasterBand(1)
outData = numpy.zeros((rows,cols), numpy.int16)


for i in range(0, rows):
    for j in range(0, cols):

    if cropData[i,j] in listAg:
        outData[i,j] = 100
    elif cropData[i,j] in listNotAg:
        outData[i,j] = -100
    else:
        outData[i,j] = 0


# write the data
outBand.WriteArray(outData, 0, 0)

# flush data to disk, set the NoData value and calculate stats
outBand.FlushCache()
outBand.SetNoDataValue(-99)

# georeference the image and set the projection
outDs.SetGeoTransform(inDs.GetGeoTransform())
outDs.SetProjection(inDs.GetProjection())

del outData

1
+1 per arrossire - mi batteva la testa contro il muro cercando di capire come "salvare" la cosa!
badgley,

Ho dovuto aggiungere outDs = Noneper salvarlo
JaakL il

23

Ho finalmente trovato la soluzione che ho tratto da questa discussione ( http://osgeo-org.1560.n6.nabble.com/gdal-dev-numpy-array-to-raster-td4354924.html ). Mi piace perché posso passare direttamente da una matrice numpy a un file tif raster. Sarei molto grato per i commenti che potrebbero migliorare la soluzione. Lo pubblicherò qui nel caso in cui qualcun altro cerchi una risposta simile.

import numpy as np
from osgeo import gdal
from osgeo import gdal_array
from osgeo import osr
import matplotlib.pylab as plt

array = np.array(( (0.1, 0.2, 0.3, 0.4),
                   (0.2, 0.3, 0.4, 0.5),
                   (0.3, 0.4, 0.5, 0.6),
                   (0.4, 0.5, 0.6, 0.7),
                   (0.5, 0.6, 0.7, 0.8) ))
# My image array      
lat = np.array(( (10.0, 10.0, 10.0, 10.0),
                 ( 9.5,  9.5,  9.5,  9.5),
                 ( 9.0,  9.0,  9.0,  9.0),
                 ( 8.5,  8.5,  8.5,  8.5),
                 ( 8.0,  8.0,  8.0,  8.0) ))
lon = np.array(( (20.0, 20.5, 21.0, 21.5),
                 (20.0, 20.5, 21.0, 21.5),
                 (20.0, 20.5, 21.0, 21.5),
                 (20.0, 20.5, 21.0, 21.5),
                 (20.0, 20.5, 21.0, 21.5) ))
# For each pixel I know it's latitude and longitude.
# As you'll see below you only really need the coordinates of
# one corner, and the resolution of the file.

xmin,ymin,xmax,ymax = [lon.min(),lat.min(),lon.max(),lat.max()]
nrows,ncols = np.shape(array)
xres = (xmax-xmin)/float(ncols)
yres = (ymax-ymin)/float(nrows)
geotransform=(xmin,xres,0,ymax,0, -yres)   
# That's (top left x, w-e pixel resolution, rotation (0 if North is up), 
#         top left y, rotation (0 if North is up), n-s pixel resolution)
# I don't know why rotation is in twice???

output_raster = gdal.GetDriverByName('GTiff').Create('myraster.tif',ncols, nrows, 1 ,gdal.GDT_Float32)  # Open the file
output_raster.SetGeoTransform(geotransform)  # Specify its coordinates
srs = osr.SpatialReference()                 # Establish its coordinate encoding
srs.ImportFromEPSG(4326)                     # This one specifies WGS84 lat long.
                                             # Anyone know how to specify the 
                                             # IAU2000:49900 Mars encoding?
output_raster.SetProjection( srs.ExportToWkt() )   # Exports the coordinate system 
                                                   # to the file
output_raster.GetRasterBand(1).WriteArray(array)   # Writes my array to the raster

output_raster.FlushCache()

3
La "rotazione è in due volte" per tenere conto dell'effetto di un bit ruotato di y su xe del bit ruotato di x su y. Vedi lists.osgeo.org/pipermail/gdal-dev/2011-July/029449.html che cerca di spiegare le interrelazioni tra i parametri di "rotazione".
Dave X,

Questo post è davvero utile, grazie. Nel mio caso, tuttavia, sto ottenendo un file tif completamente nero quando lo apro come immagine all'esterno di ArcGIS. Il mio riferimento spaziale è la British National Grid (EPSG = 27700) e le unità sono metri.
FaCoffee

Ho pubblicato una domanda qui: gis.stackexchange.com/questions/232301/…
FaCoffee

Hai scoperto come impostare la codifica IAU2000: 49900 Mars?
K.-Michael Aye il

4

C'è anche una bella soluzione nel ricettario ufficiale GDAL / OGR per Python.

Questa ricetta crea un raster da un array

import gdal, ogr, os, osr
import numpy as np


def array2raster(newRasterfn,rasterOrigin,pixelWidth,pixelHeight,array):

    cols = array.shape[1]
    rows = array.shape[0]
    originX = rasterOrigin[0]
    originY = rasterOrigin[1]

    driver = gdal.GetDriverByName('GTiff')
    outRaster = driver.Create(newRasterfn, cols, rows, 1, gdal.GDT_Byte)
    outRaster.SetGeoTransform((originX, pixelWidth, 0, originY, 0, pixelHeight))
    outband = outRaster.GetRasterBand(1)
    outband.WriteArray(array)
    outRasterSRS = osr.SpatialReference()
    outRasterSRS.ImportFromEPSG(4326)
    outRaster.SetProjection(outRasterSRS.ExportToWkt())
    outband.FlushCache()


def main(newRasterfn,rasterOrigin,pixelWidth,pixelHeight,array):
    reversed_arr = array[::-1] # reverse array so the tif looks like the array
    array2raster(newRasterfn,rasterOrigin,pixelWidth,pixelHeight,reversed_arr) # convert array to raster


if __name__ == "__main__":
    rasterOrigin = (-123.25745,45.43013)
    pixelWidth = 10
    pixelHeight = 10
    newRasterfn = 'test.tif'
    array = np.array([[ 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, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1],
                      [ 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1],
                      [ 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1],
                      [ 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1],
                      [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]])


    main(newRasterfn,rasterOrigin,pixelWidth,pixelHeight,array)

Questa ricetta è buona ma c'è un problema con il file tiff finale. I valori lat-lon dei pixel non sono corretti.
Shubham_geo,


Una cosa che ho riscontrato è che il modo in cui hai citato cambierà sicuramente l'array in un raster con facilità. Ma dobbiamo georeferenziare questo raster con in alto a sinistra e in basso a destra coordinati usando gdal_translate. Un modo per farlo è seguire i due passaggi: 1) Per prima cosa trovare i valori lat-lon in alto a sinistra e in basso a destra tramite gdalinfo 2) Quindi, tramite gdal_translate utilizzare il geotiff (generato con l'approccio sopra menzionato di convertire l'array in raster) per georeferenziarlo con le coordinate lat-lon in alto a sinistra e in basso a destra.
Shubham_geo, il

0

Un'alternativa all'approccio suggerito nelle altre risposte è l'uso del rasteriopacchetto. Ho avuto problemi a generarli usando gdale ho trovato utile questo sito .

Supponendo che tu abbia un altro file tif ( other_file.tif) e un array numpy ( numpy_array) che abbia la stessa risoluzione ed estensione di questo file, questo è l'approccio che ha funzionato per me:

import rasterio as rio    

with rio.open('other_file.tif') as src:
    ras_data = src.read()
    ras_meta = src.profile

# make any necessary changes to raster properties, e.g.:
ras_meta['dtype'] = "int32"
ras_meta['nodata'] = -99

with rio.open('outname.tif', 'w', **ras_meta) as dst:
    dst.write(numpy_array, 1)
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.