GDAL e Python: come ottenere coordinate per tutte le celle con un valore specifico?


12

Ho una griglia binaria Arc / Info --- in particolare, un raster di accumulo del flusso ArcGIS --- e vorrei identificare tutte le celle con un valore specifico (o in un intervallo di valori). Alla fine, vorrei uno shapefile di punti che rappresentano queste celle.

Posso usare QGIS per aprire hdr.adf e ottenere questo risultato, il flusso di lavoro è:

  • QGIS> Menu raster> Calcolatrice raster (contrassegnare tutti i punti con il valore target)
  • QGIS> Menu raster> Poligonizza
  • QGIS> Menu vettoriale> Sottomenu Geometria> Centroidi poligonali
  • Modifica i centroidi per eliminare i poli centroidi indesiderati (quelli = 0)

Questo approccio "fa il lavoro", ma non mi piace perché crea 2 file che devo eliminare, quindi devo rimuovere i record indesiderati dal file di forma dei centroidi (cioè quelli = 0).

Una domanda esistente si avvicina a questo argomento, ma è su misura per ArcGIS / ArcPy e mi piacerebbe rimanere nello spazio FOSS.

Qualcuno ha una ricetta / script GDAL / Python esistente che interroga i valori della cella di un raster e quando viene trovato un valore target --- o un valore in un intervallo target ---, un record viene aggiunto a uno shapefile? Ciò non solo eviterebbe l'interazione dell'interfaccia utente, ma creerebbe un risultato pulito in un unico passaggio.

Ci ho provato lavorando contro una delle presentazioni di Chris Garrard , ma il lavoro raster non è nella mia timoniera e non voglio ingombrare la domanda con il mio codice debole.

Se qualcuno volesse il set di dati esatto con cui giocare, lo metto qui come .zip .


[Modifica note] Lasciandolo alle spalle per i posteri. Vedi gli scambi di commenti con om_henners. Fondamentalmente sono stati capovolti i valori x / y (riga / colonna). La risposta originale aveva questa linea:

(y_index, x_index) = np.nonzero(a == 1000)

invertito, in questo modo:

(x_index, y_index) = np.nonzero(a == 1000)

Quando ho riscontrato per la prima volta il problema illustrato nello screenshot, mi chiedevo se avevo implementato la geometria in modo errato e ho sperimentato capovolgendo i valori delle coordinate x / y in questa riga:

point.SetPoint(0, x, y)

..come..

point.SetPoint(0, y, x)

Tuttavia, ciò non ha funzionato. E non pensavo di provare a lanciare i valori nell'espressione Numpy di om_henners, credendo erroneamente che lanciarli su entrambe le righe fosse equivalente. Penso che il vero problema sia relativo ai valori x_sizee y_size, rispettivamente 30e -30, che vengono applicati quando gli indici di riga e colonna vengono utilizzati per calcolare le coordinate dei punti per le celle.

[Modifica originale]

@om_henners, sto provando la tua soluzione, di concerto con un paio di destinatari per creare shapefile di punti usando ogr ( invisibleroads.com , Chris Garrard ), ma sto riscontrando un problema per cui i punti appaiono come se si riflettessero su una linea che passa attraverso 315/135 gradi.

Punti azzurri : creati dal mio approccio QGIS , sopra

Punti viola : creati dal codice py GDAL / OGR , di seguito

inserisci qui la descrizione dell'immagine


[Risolto]

Questo codice Python implementa la soluzione completa proposta da @om_henners. L'ho provato e funziona. Grazie uomo!


from osgeo import gdal
import numpy as np
import osgeo.ogr
import osgeo.osr

path = "D:/GIS/greeneCty/Greene_DEM/GreeneDEM30m/flowacc_gree/hdr.adf"
print "\nOpening: " + path + "\n"

r = gdal.Open(path)
band = r.GetRasterBand(1)

(upper_left_x, x_size, x_rotation, upper_left_y, y_rotation, y_size) = r.GetGeoTransform()

a = band.ReadAsArray().astype(np.float)

# This evaluation makes x/y arrays for all cell values in a range.
# I knew how many points I should get for ==1000 and wanted to test it.
(y_index, x_index) = np.nonzero((a > 999) & (a < 1001))

# This evaluation makes x/y arrays for all cells having the fixed value, 1000.
#(y_index, x_index) = np.nonzero(a == 1000)

# DEBUG: take a look at the arrays..
#print repr((y_index, x_index))

# Init the shapefile stuff..
srs = osgeo.osr.SpatialReference()
#srs.ImportFromProj4('+proj=utm +zone=15 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs')
srs.ImportFromWkt(r.GetProjection())

driver = osgeo.ogr.GetDriverByName('ESRI Shapefile')
shapeData = driver.CreateDataSource('D:/GIS/01_tutorials/flow_acc/ogr_pts.shp')

layer = shapeData.CreateLayer('ogr_pts', srs, osgeo.ogr.wkbPoint)
layerDefinition = layer.GetLayerDefn()

# Iterate over the Numpy points..
i = 0
for x_coord in x_index:
    x = x_index[i] * x_size + upper_left_x + (x_size / 2) #add half the cell size
    y = y_index[i] * y_size + upper_left_y + (y_size / 2) #to centre the point

    # DEBUG: take a look at the coords..
    #print "Coords: " + str(x) + ", " + str(y)

    point = osgeo.ogr.Geometry(osgeo.ogr.wkbPoint)
    point.SetPoint(0, x, y)

    feature = osgeo.ogr.Feature(layerDefinition)
    feature.SetGeometry(point)
    feature.SetFID(i)

    layer.CreateFeature(feature)

    i += 1

shapeData.Destroy()

print "done! " + str(i) + " points found!"

1
Suggerimento rapido per il codice: è possibile utilizzare la proiezione raster come proiezione del file di forma con srs.ImportFromWkt(r.GetProjection())(anziché dover creare una proiezione da una stringa di proj nota).
om_henners,

Il tuo codice fa tutto quello che sto cercando tranne che non include il valore della cella raster poiché hai scritto il tuo filtro numpy per includere solo valori = 1000. Potresti indicarmi la giusta direzione per includere i valori della cella raster nella produzione? Grazie!
Brent Edwards,

Risposte:


19

In GDAL puoi importare il raster come un array intorpidito.

from osgeo import gdal
import numpy as np

r = gdal.Open("path/to/raster")
band = r.GetRasterBand(1) #bands start at one
a = band.ReadAsArray().astype(np.float)

Quindi usando numpy è semplice ottenere che gli indici di un array corrispondano a un'espressione boolana:

(y_index, x_index) = np.nonzero(a > threshold)
#To demonstate this compare a.shape to band.XSize and band.YSize

Dalla geotrasformazione raster possiamo ottenere informazioni come le coordinate x e y in alto a sinistra e le dimensioni delle celle.

(upper_left_x, x_size, x_rotation, upper_left_y, y_rotation, y_size) = r.GetGeoTransform()

La cella in alto a sinistra corrisponde a a[0, 0]. La dimensione Y sarà sempre negativa, quindi usando gli indici xey puoi calcolare le coordinate di ogni cella in base agli indici.

x_coords = x_index * x_size + upper_left_x + (x_size / 2) #add half the cell size
y_coords = y_index * y_size + upper_left_y + (y_size / 2) #to centre the point

Da qui è abbastanza semplice creare un file di forma usando OGR. Per alcuni esempi di codice, consultare questa domanda su come generare un nuovo set di dati con informazioni sui punti.


Ehi amico, sto avendo un piccolo problema con l'implementazione di questo. Ho aggiornato la domanda per includere il codice che sto usando e uno screeshot di quello che sto ricevendo. Fondamentalmente il codice .py sta creando un'immagine speculare (posizionamento dei punti) di ciò che l'approccio QGIS sta generando. I punti nella mia implementazione non rientrano nei limiti raster, quindi il problema deve riguardare il mio codice. : = Spero che tu possa far luce. Grazie!
elrobis,

Mi dispiace per quello - completamente mio male. Quando importi un raster in GDAL le righe sono nella direzione y e le colonne nella direzione x. Ho aggiornato il codice sopra, ma il trucco è ottenere gli indici con(y_index, x_index) = np.nonzero(a > threshold)
om_henners il

1
Inoltre, per ogni evenienza, nota l'aggiunta della metà della dimensione della cella alle coordinate in entrambe le direzioni per centrare il punto nella cella.
om_henners,

Sì, quello era il problema. Quando ho riscontrato per la prima volta quel bug (come mostrato nella schermata), mi chiedevo se avevo implementato la geometria del punto in modo errato, quindi ho provato a lanciare x / y come y / x quando ho realizzato .shp--- solo che non lavoro, né era vicino. Non sono rimasto scioccato poiché il valore x è in centinaia di migliaia e y è in milioni, quindi mi ha lasciato piuttosto confuso. Non pensavo di provare a riportarli indietro all'espressione Numpy. Grazie mille per il tuo aiuto, è fantastico. Esattamente quello che volevo. :)
elrobis il

4

Perché non usare la cassetta degli attrezzi Sexante in QGIS? È un po 'come il Model Builder per ArcGIS. In questo modo, è possibile eseguire operazioni a catena e trattarle come un'unica operazione. È possibile automatizzare la cancellazione di file intermedi e la rimozione di record indesiderati se non sbaglio.


1

Potrebbe essere utile importare i dati su Postgis (con supporto raster) e utilizzare le relative funzioni. Questa esercitazione può contenere elementi necessari.

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.