46

I am working on a computational model of the abundance of wild pollinators across a landscape. The model itself is complete, and I am now struggling with a post-processing step.

I have my GDAL pollinator supply raster that looks something like this (lighter colors mean higher pollinator visitation to a pixel):

Greyscale raster representing pollinator supply on a landscape

And I have an OGR shapefile of points representing sample locations on the landscape:

enter image description here

I'm trying to run some analysis on the pixels under these points, but to do so, I need to be able to extract the value of a pixel under a point.

Is it possible to extract the value of a pixel under a point using only OGR and GDAL through Python? I would prefer to avoid reading the entire raster into memory through ReadAsArray(), as my output rasters are very, very large (too large to fit into memory).

I noticed this post, which is similar, but requires a command-line call.

6
  • 2
    What about ReadAsArray() and only reading in the point? So only read the single cell that you are interested in? You would need to convert from the point coords to pixel space and extract the necessary cell.
    – Jay Laura
    Jan 11, 2013 at 22:14
  • 1
    Look at the code for gdalsrsinfo, it shows you how to use GDALInvertGeoTransform() and switch between geographic space and pixel space: trac.osgeo.org/gdal/browser/trunk/gdal/apps/gdalsrsinfo.cpp
    – user10353
    Jan 11, 2013 at 23:18
  • 1
    If you don't mind using PostGIS, see this. It is extremely fast and is just 1 SQL line.
    – mlt
    Jul 16, 2013 at 18:57
  • I'll keep that in mind if I come across this problem and have access to a PostGIS database! I didn't for this particular problem, so the GDAL solution below did the trick. Thanks, though!
    – James
    Aug 5, 2013 at 19:03
  • @kyle I don't know if things have changed but it looks like it is GDALInvGeoTransform not invert and this is an example.
    – mlt
    Sep 19, 2013 at 22:29

1 Answer 1

67

You can use the gdal.Dataset or gdal.Band ReadRaster method. See the GDAL and OGR API tutorials and the example below. ReadRaster does not use/require numpy, the return value is raw binary data and needs to be unpacked using the standard python struct module.

An example:

from math import floor
from osgeo import gdal,ogr
import struct

src_filename = '/tmp/test.tif'
shp_filename = '/tmp/test.shp'

src_ds=gdal.Open(src_filename) 
gt_forward=src_ds.GetGeoTransform()
gt_reverse=gdal.InvGeoTransform(gt_forward)
rb=src_ds.GetRasterBand(1)

ds=ogr.Open(shp_filename)
lyr=ds.GetLayer()
for feat in lyr:
    geom = feat.GetGeometryRef()
    mx,my=geom.GetX(), geom.GetY()  #coord in map units

    #Convert from map to pixel coordinates.
    px, py = gdal.ApplyGeoTransform(gt_reverse, mx, my)
    px = floor(px) #x pixel
    py = floor(py) #y pixel

    structval=rb.ReadRaster(px,py,1,1,buf_type=gdal.GDT_UInt16) #Assumes 16 bit int aka 'short'
    intval = struct.unpack('h' , structval) #use the 'short' format code (2 bytes) not int (4 bytes)
    
    print intval[0] #intval is a tuple, length=1 as we only asked for 1 pixel value

Alternatively, since the reason you gave for not using numpy was to avoid reading the entire array in using ReadAsArray(), below is an example that does use numpy and does not read the entire raster into memory, it only reads the raster value at the given point. It uses the built-in gdal.ApplyGeoTransform() function in order to deal with axes rotations.

from math import floor
from osgeo import gdal,ogr

src_filename = '/tmp/test.tif'
shp_filename = '/tmp/test.shp'

src_ds=gdal.Open(src_filename) 
gt_forward=src_ds.GetGeoTransform()
gt_reverse=gdal.InvGeoTransform(gt_forward)
rb=src_ds.GetRasterBand(1)

ds=ogr.Open(shp_filename)
lyr=ds.GetLayer()
for feat in lyr:
    geom = feat.GetGeometryRef()
    mx,my=geom.GetX(), geom.GetY()  #coord in map units

    #Convert from map to pixel coordinates.
    px, py = gdal.ApplyGeoTransform(gt_reverse, mx, my)
    px = floor(px) #x pixel
    py = floor(py) #y pixel

    intval=rb.ReadAsArray(px,py,1,1)
    print intval[0] #intval is a numpy array, length=1 as we only asked for 1 pixel value

2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.