8

I clipped my TIFF image to smaller TIFF via using a shapefile as mask with rasterio Python library. After that my square shaped clip has bigger extent than shapefile and outside the the shapefile area is black and valued as 0. How can I change it to NODATA values?

enter image description here

2
  • 1
    getting NaN is easy just divide them by 0 or Inf
    – Ian Turton
    Commented Jul 29, 2020 at 16:20
  • 2
    What is your NoData value set to at the moment? What is the data type? Is it 8 bit 3 band? Was your original 8 bit 3 band or did it have a 4th band? What was the NoData value in the original? BTW NaN is not NoData, NaN is part of the complex floating point specification, it means Not a Number, complex float supports infinity, negative infinity and not a number values, simple floating points do not. NoData is a GIS concept used to make images transparent or to exclude from geoprocessing where cells have not been assigned a legitimate value. Commented Jul 30, 2020 at 1:36

3 Answers 3

5

You need to open the source file to read data values and metadata, then read the band data as numpy array, make the required changes to the numpy array and then save numpy array to a new dataset file:

import numpy as np
import rasterio

def fix_no_data_value(input_file, output_file, no_data_value=0):
    with rasterio.open(input_file, "r+") as src:
        src.nodata = no_data_value
        with rasterio.open(output_file, 'w',  **src.profile) as dst:
            for i in range(1, src.count + 1):
                band = src.read(i)
                band = np.where(band==no_data_value,no_data_value,band)
                dst.write(band,i)

# usage example 
input_file = "input_image.tif"
output_file = "output.tif"
no_data_value=0
fix_no_data_value(input_file, output_file,no_data_value)  

References:

https://rasterio.groups.io/g/main/topic/change_the_nodata_value_in_a/28801885 https://rasterio.readthedocs.io/en/latest/topics/writing.html

1

I suspect that you can just set nodata when you re-write the image, then rasterio will just treat any 0 as a masked pixel. If you want to modify 0 to be your preferred nodata value, then it would be cleaner to fix your cropping code rather than changing your data retrospectively.

Here's a snippet that works for me. Assuming your code is similar, you can re-run it and specify nodata in rasterio.mask.

def crop_region(input_path : str, output_path : str, geometry : dict, nodata: int =255):
    
    os.makedirs(os.path.dirname(output_path), exist_ok=True)

    with rasterio.open(image_path) as src:
        out_image, out_transform = rasterio.mask.mask(src,
                                                      [geometry],
                                                      crop=True,
                                                      nodata=nodata)
        out_meta = src.meta
        
        out_meta.update({"driver": "GTiff",
             "height": out_image.shape[1],
             "width": out_image.shape[2],
             "nodata": nodata,
             "transform": out_transform})
        
        with rasterio.open(output_path, "w", **out_meta) as dest:
            dest.write(out_image) 

This takes an image and output path and a geometry polygon (that you've transformed to your image CRS). First we crop the image using rasterio mask, you can provide nodata here to specify a fill value for pixels that fall outside the crop region.

When you save it, you also need to specify the nodata value in the image, which will be saved to the geotiff. If you load this data again, make sure you load in masked mode:

with rasterio.open(out_path) as src:
    data = src.read(masked=True)

enter image description here

(White areas here are correctly masked and not plotted). With masked=False:

enter image description here

If you want to modify which nodata value is used, this is easy with update:

with rasterio.open(out_path) as src:
    data = src.read(masked=False)
    
    out_meta = src.meta
    out_meta.update({
             "nodata": 0})
    
    with rasterio.open("masked.tif", 'w', **out_meta) as dst:
        dst.write(src.read())
        

with nodata set to 4, this time the 255 pixels are read as valid:

enter image description here

0

I assume you are using the code recomended in the documentation of rasterio for this: masking a raster using a shapefile

In this part of the code I just added the parameter nodata and set it to np.nan.

import numpy as np

with rasterio.open("tests/data/RGB.byte.tif") as src:
out_image, out_transform = rasterio.mask.mask(src, shapes, crop=True, nodata=np.nan)
out_meta = src.meta

Your Answer

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

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