11

I'm not a GIS person. I haven't found or at least identified a solution to my problem, so decided to ask.

I have a big rasters and a list of thousands of coordinates. I need to find the pixel location corresponding to a given lat/lon pair in the raster, and then clip an NxN rectangle with the pixel located in the middle of it and save the clip on disk. I haven't for the life of me figured out yet how I could find the pixel location using coordinates, and I've been on it a few days now. I found that with snappy I should be able to do something like that, but unfortunately it doesn't run on my system (anaconda3 with Python 3.6).

How can I go about solving this problem?

4
  • Going from coordinates in a given CRS to array indices and vice-versa is what the affine library does. Any rasterio dataset has an Affine which can be accessed via the transform attribute Oct 22, 2018 at 21:50
  • You have a tag for GDAL, is that the API you're interested in? In GDAL you open your raster and retrieve the GeoTransform which is 6 double values see the tutorial gdal.org/gdal_tutorial.html, column is ( X - GeoTransform[0] ) / GeoTransform[1] and row is ( GeoTransform[3] - Y ) / GeoTransform[5] because the reference is top left and Y cell sizes are negative. Oct 22, 2018 at 22:45
  • That formula only works if the raster is georeferenced docs.qgis.org/2.18/en/docs/training_manual/forestry/… and is in the same spatial reference as your input points. Oct 22, 2018 at 22:57
  • Sorry, should've been more explicit about the APIs. I'm interested in both GDAL and rasterio, although I prefer the latter. In any case, Luke's answer is exactly what I've been trying to do, and it's with rasterio. Thanks anyways for your help!
    – GISjoe
    Oct 23, 2018 at 16:33

3 Answers 3

21

A rasterio way of doing this is pretty simple. Note this requires your raster be in the same projection as your coordinates. You can of course project your coordinates on the fly, but that's another question...

import rasterio as rio

infile = r"C:\Temp\test.tif"
outfile = r'C:\Temp\test_{}.tif'
coordinates = (
    (130.5, -25.5) , # lon, lat of ~centre of Australia
    (146.0, -42.0) , # lon, lat of ~centre of Tasmania
)

# Your NxN window
N = 3

# Open the raster
with rio.open(infile) as dataset:

    # Loop through your list of coords
    for i, (lon, lat) in enumerate(coordinates):

        # Get pixel coordinates from map coordinates
        py, px = dataset.index(lon, lat)
        print('Pixel Y, X coords: {}, {}'.format(py, px))

        # Build an NxN window
        window = rio.windows.Window(px - N//2, py - N//2, N, N)
        print(window)

        # Read the data in the window
        # clip is a nbands * N * N numpy array
        clip = dataset.read(window=window)

        # You can then write out a new file
        meta = dataset.meta
        meta['width'], meta['height'] = N, N
        meta['transform'] = rio.windows.transform(window, dataset.transform)

        with rio.open(outfile.format(i), 'w', **meta) as dst:
            dst.write(clip)
3
  • Thanks, Luke, your answer is exactly what I've been trying to do with no success. I just tried it with the raster and coordinates I have, and it seems to be working like a charm.
    – GISjoe
    Oct 23, 2018 at 16:35
  • do you if this returns nearest neighbour? I cant figure it out..wondering if this requires exact coordinates (within a certain precision) or returns NN for a given coordinate
    – Derek Eden
    Mar 14, 2021 at 20:01
  • What do you mean "nearest neighbour"? If a coordinate is outside the extent of a raster?
    – user2856
    Mar 14, 2021 at 20:43
13

Another way to do this is to use the rasterio.transform.rowcol() method described in the rasterio transform docs.

Example:

import numpy as np
import rasterio

xs = np.array([130.5, 146.0])
ys = np.array([-25.5, -42.0])

with rasterio.open("my.tif") as src:
    rows, cols = rasterio.transform.rowcol(src.transform, xs, ys)
5

Another option (as per March 2020) could be using xarray.open_rasterio in combination with the method .sel(). Like this you have yet another very short solution:

import xarray as xr

# We define a location
lon1, lat1 = (-0.25, 39.95)    

# In this example I am reading a file with (time, x, y) as dimensions
xarr = xr.open_rasterio(path_to_tiff_file)

# Slice one of the bands
img = xarr[0, :, :] 

#Use the .sel() method to retrieve the value of the nearest cell close to your POI
val = img.sel(x=lon1, y=lat1, method="nearest")    
print("This is val: ", val)

Which returns the following xarray.DataArray description:

>>> This is val:  <xarray.DataArray ()>
array(0.249235)
Coordinates:
    band     int64 1
    y        float64 39.98
    x        float64 -0.2087
Attributes:
    transform:      (0.13190025669672106, 0.0, -60.553065717372434, 0.0, -0.1...
    crs:            +init=epsg:4326
    res:            (0.13190025669672106, 0.13190025669672106)
    is_tiled:       0
    nodatavals:     (nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, n...
    scales:         (1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1...
    offsets:        (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0...
    descriptions:   ('0[-] SFC="Ground or water surface"', '0[-] SFC="Ground ...
    AREA_OR_POINT:  Area

And if we open such TIFF file in QGIS and explore the location (i.e. 'Band 001') we can see its value:

enter image description here

So it seems to be working as expected. The only thing here is that I am not sure how fast this compact solution would be in the event of making this operation, say, thousands of times.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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