43

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.

  • 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 '13 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 '13 at 23:18
  • If you don't mind using PostGIS, see this. It is extremely fast and is just 1 SQL line. – mlt Jul 16 '13 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 '13 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 '13 at 22:29
58

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 osgeo import gdal,ogr
import struct

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

src_ds=gdal.Open(src_filename) 
gt=src_ds.GetGeoTransform()
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.
    #Only works for geotransforms with no rotation.
    px = int((mx - gt[0]) / gt[1]) #x pixel
    py = int((my - gt[3]) / gt[5]) #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 uses numpy and does not read the entire raster in.

from osgeo import gdal,ogr
import struct

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

src_ds=gdal.Open(src_filename) 
gt=src_ds.GetGeoTransform()
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.
    #Only works for geotransforms with no rotation.
    px = int((mx - gt[0]) / gt[1]) #x pixel
    py = int((my - gt[3]) / gt[5]) #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
  • And how can you save as csv, table or other object? An object with the same length of coordinates objetct – gonzalez.ivan90 Jan 26 '18 at 18:12
  • Here's the question, Luke. Thanks! gis.stackexchange.com/questions/269603/… – gonzalez.ivan90 Jan 29 '18 at 18:24
  • the lines that assign px/py are wrong in the case that mx/my lies outside the bounds of rb, because int(-0.5) == 0. You need floor(...), and then you need to check that neither of px/py are less than zero (or do it before calling int()), because negative indices work (they get the other side of the array). I would love to know if there is a neater way to deal with this problem. Also, how do you re-write those lines so that they deal with rotations correctly? – naught101 Mar 7 at 3:55

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.