Skip to content

Reader Module

sensingpy.reader provides classes for loading geospatial raster data from NetCDF and GeoTIFF files, plus a convenience open() function that dispatches to the right reader automatically.


ImageReader

Base class that defines the common interface for all readers.

ImageReader

Base class for reading geospatial images from different file formats.

This is an abstract base class that defines the interface for all image readers. Concrete subclasses must implement the read method.

Notes

This class follows the Strategy pattern to provide different algorithms for reading various geospatial file formats while maintaining a consistent interface.

Functions

read
read(filename: str) -> Image

Read an image file and convert it to an Image object.

Parameters:

Name Type Description Default
filename str

Path to the image file to be read

required

Returns:

Type Description
Image

A processed image with spatial reference information

Raises:

Type Description
NotImplementedError

This is an abstract method that must be implemented by subclasses

Source code in sensingpy/reader.py
def read(self, filename: str) -> Image:
    """
    Read an image file and convert it to an Image object.

    Parameters
    ----------
    filename : str
        Path to the image file to be read

    Returns
    -------
    Image
        A processed image with spatial reference information

    Raises
    ------
    NotImplementedError
        This is an abstract method that must be implemented by subclasses
    """
    raise NotImplementedError("This method must be implemented by subclasses")

NetCDFReader

Reads multidimensional NetCDF files and exposes bands as Image objects.

NetCDFReader

Bases: ImageReader

Reader for NetCDF image files with geospatial metadata.

This class reads NetCDF (.nc, .nc4, .netcdf) files and extracts image data, coordinate systems, and spatial metadata to create an Image object. It handles various CF-convention metadata structures and fallback options for CRS information.

Notes

NetCDF files can store coordinate reference system (CRS) information in several locations according to the CF conventions. This reader searches for CRS information in coordinates, variables, and global attributes, in that order.

Functions

read
read(filename: str) -> Image

Read a NetCDF file and convert it to an Image object.

Parameters:

Name Type Description Default
filename str

Path to the NetCDF file

required

Returns:

Type Description
Image

An Image object containing the data, coordinates, and CRS information

Notes

The reader searches for CRS information in the following locations: 1. DataArrays with a 'crs_wkt' attribute in coordinates 2. Variables with a 'crs_wkt' attribute 3. Global attributes ('crs_wkt' or 'proj4_string')

If no CRS information is found, a default grid mapping variable is created.

Source code in sensingpy/reader.py
def read(self, filename: str) -> Image:
    """
    Read a NetCDF file and convert it to an Image object.

    Parameters
    ----------
    filename : str
        Path to the NetCDF file

    Returns
    -------
    Image
        An Image object containing the data, coordinates, and CRS information

    Notes
    -----
    The reader searches for CRS information in the following locations:
    1. DataArrays with a 'crs_wkt' attribute in coordinates
    2. Variables with a 'crs_wkt' attribute
    3. Global attributes ('crs_wkt' or 'proj4_string')

    If no CRS information is found, a default grid mapping variable is created.
    """
    grid_mapping = "projection"

    with xr.open_dataset(filename) as src:
        crs = None
        crs_var_name = None

        # Search for a DataArray with crs_wkt attribute in coordinates
        for coord_name, coord in src.coords.items():
            if "crs_wkt" in coord.attrs:
                crs = pyproj.CRS.from_wkt(coord.attrs["crs_wkt"])
                crs_var_name = coord_name
                break

        # If not found in coordinates, search in variables
        if crs is None:
            for var_name, var in src.data_vars.items():
                if "crs_wkt" in var.attrs:
                    crs = pyproj.CRS.from_wkt(var.attrs["crs_wkt"])
                    crs_var_name = var_name
                    src.coords[var_name] = var
                    break

        # If still not found, look in global attributes
        if crs is None:
            if "crs_wkt" in src.attrs:
                crs = pyproj.CRS.from_wkt(src.attrs["crs_wkt"])
            elif "proj4_string" in src.attrs:
                crs = pyproj.CRS.from_proj4(src.attrs["proj4_string"])

        # If no variable name was found for the projection, use the default
        if crs_var_name is None:
            crs_var_name = grid_mapping
            src.coords[grid_mapping] = xr.DataArray(0, attrs=crs.to_cf())

        # Ensure all variables have the grid_mapping attribute
        for var in src.data_vars:
            src[var].attrs["grid_mapping"] = crs_var_name

        src.attrs["grid_mapping"] = crs_var_name

        return Image(data=src, crs=crs)

GeoTIFFReader

Reads single- or multi-band GeoTIFF files and exposes bands as Image objects.

GeoTIFFReader

Bases: ImageReader

Reader for GeoTIFF image files with geospatial metadata.

This class reads GeoTIFF (.tif, .tiff, .geotiff) files and extracts image data, coordinate systems, and spatial metadata to create an Image object. It handles conversion from GDAL/rasterio representation to xarray format.

Notes

The class preserves band-specific metadata, nodata values, and coordinates while transforming raster data into xarray's data model with explicit dimensions and coordinates.

Functions

read
read(filename: str) -> Image

Read a GeoTIFF file and convert it to an Image object.

Parameters:

Name Type Description Default
filename str

Path to the GeoTIFF file

required

Returns:

Type Description
Image

An Image object containing the data, coordinates, and CRS information

Notes

This method extracts GeoTIFF metadata including: - Spatial reference information (CRS) - Band data and descriptions - Nodata values - TIFF tags and band-specific metadata

Source code in sensingpy/reader.py
def read(self, filename: str) -> Image:
    """
    Read a GeoTIFF file and convert it to an Image object.

    Parameters
    ----------
    filename : str
        Path to the GeoTIFF file

    Returns
    -------
    Image
        An Image object containing the data, coordinates, and CRS information

    Notes
    -----
    This method extracts GeoTIFF metadata including:
    - Spatial reference information (CRS)
    - Band data and descriptions
    - Nodata values
    - TIFF tags and band-specific metadata
    """
    grid_mapping = "projection"

    with rasterio.open(filename) as src:
        crs = pyproj.CRS.from_proj4(src.crs.to_proj4())
        coords = self._prepare_coords(src, crs, grid_mapping)
        variables = self._prepare_vars(src, coords, grid_mapping)

        # Create global dataset attributes
        attrs = {}

        # Add nodata values
        for i in range(1, src.count + 1):
            nodata = src.nodatavals[i - 1]
            if nodata is not None:
                attrs[f"_FillValue_band_{i}"] = nodata

        # Add other relevant metadata from GeoTIFF file
        for key, value in src.tags().items():
            # Filter some tags that might cause problems or aren't relevant
            if key not in ["TIFFTAG_DATETIME", "TIFFTAG_SOFTWARE"]:
                attrs[f"tiff_{key}"] = value

        # Add band metadata summary
        for i in range(1, src.count + 1):
            band_tags = src.tags(i)
            for tag_key, tag_value in band_tags.items():
                attrs[f"band_{i}_{tag_key}"] = tag_value

        attrs["grid_mapping"] = grid_mapping

        # Create dataset with all attributes
        dataset = xr.Dataset(data_vars=variables, coords=coords, attrs=attrs)

        return Image(data=dataset, crs=crs)

open

open

open(filename: str) -> Image

Open an image file using the appropriate reader based on file extension.

This factory function determines the correct reader to use based on the file extension and delegates to the appropriate reader class.

Parameters:

Name Type Description Default
filename str

Path to the image file to be opened

required

Returns:

Type Description
Image

An Image object containing the data, coordinates, and CRS information

Raises:

Type Description
ValueError

If the file format is not supported based on its extension

Examples:

>>> from sensingpy import reader
>>> img = reader.open('example.tif')
>>> img.plot()
>>> # Open a NetCDF file
>>> img = reader.open('example.nc')
>>> print(img.band_names)
Source code in sensingpy/reader.py
def open(filename: str) -> Image:
    """
    Open an image file using the appropriate reader based on file extension.

    This factory function determines the correct reader to use based on the file
    extension and delegates to the appropriate reader class.

    Parameters
    ----------
    filename : str
        Path to the image file to be opened

    Returns
    -------
    Image
        An Image object containing the data, coordinates, and CRS information

    Raises
    ------
    ValueError
        If the file format is not supported based on its extension

    Examples
    --------
    >>> from sensingpy import reader
    >>> img = reader.open('example.tif')
    >>> img.plot()

    >>> # Open a NetCDF file
    >>> img = reader.open('example.nc')
    >>> print(img.band_names)
    """
    extension = filename.split(".")[-1].lower()

    if extension in enums.FILE_EXTENTIONS.TIF.value:
        return GeoTIFFReader().read(filename)
    elif extension in enums.FILE_EXTENTIONS.NETCDF.value:
        return NetCDFReader().read(filename)
    else:
        raise ValueError(f"Unsupported file format: {extension}")