Skip to content

core

This module provides the core classes of the library, which aims to handle the access to the rasters delivered by different EO products (Spot-6/7, Sentinel-2, ...)

Scene

Carry all the metadata, assets paths, and sources, of one remote sensing product.

classDiagram ABC <|-- Scene class Scene{ +datetime.datetime acquisition_date +int epsg +list extent_wgs84 +dict assets_paths +dict sources +dict additional_metadata +__repr__() +get_roi_footprint() +__getattr__(name) }

Source

Sources are membres of one Scene instance, and deliver images ready to be used in otbApplication or pyotb pipelines.

classDiagram pyotb.Output <|-- Source Source <|-- Spot67Source class Source{ +__init__(root_scene, out, parent=None) +root_scene +parent +_app_stack +output_parameter_key +Source new_source(*args) } class CommonImagerySource{ +drilled(msk_vec_file, nodata=0) +cld_msk_drilled(nodata=0) +resample_over(ref_img, interpolator="nn", nodata=0) +clip_over_img(ref_img) +clip_over_vec(ref_vec) }

CommonImagerySource

Bases: Source

Base Source capabilities

Source code in scenes/core.py
class CommonImagerySource(Source):
    """
    Base Source capabilities
    """

    def drilled(
            self,
            msk_vec_file: str,
            inside: bool = True,
            nodata: Union[float, int] = 0
    ) -> CommonImagerySource:
        """
        Return the source drilled from the input vector data.

        The default behavior is that the hole is made inside the polygon.
        This can be changed setting the "inside" parameter to False.

        Args:
            msk_vec_file: input vector data filename
            inside: whether the drill is happening inside the polygon or
                outside (Default value = True)
            nodata: nodata value inside holes (Default value = 0)

        Returns:
            drilled source

        """
        if ogr_open(msk_vec_file):
            # Vector data not empty
            rasterization = pyotb.Rasterization({
                "in": msk_vec_file,
                "im": self,
                "mode": "binary",
                "mode.binary.foreground": 0 if inside else 255,
                "background": 255 if inside else 0
            })
            return self.masked(binary_mask=rasterization, nodata=nodata)
        if inside:
            return self  # Nothing but a soft copy of the source
        return self.new_source(
            pyotb.BandMathX({"il": self, "exp": "0.0 * im1"}))

    def masked(
            self,
            binary_mask: Union[str, pyotb.core.OTBObject],
            nodata: Union[float, int] = 0
    ) -> CommonImagerySource:
        """
        Return the source masked from an uint8 binary raster (0 or 1..255).

        Pixels are set to "nodata" where the mask values are 0.

        Args:
            binary_mask: input mono-band binary raster filename
            nodata: nodata value for rejected values (Default value = 0)

        Returns:
            masked source

        """
        manage_nodata = pyotb.ManageNoData({
            "in": self,
            "mode": "apply",
            "mode.apply.mask": binary_mask,
            "mode.apply.ndval": nodata
        })
        return self.new_source(binary_mask, manage_nodata)

    def resample_over(
            self,
            ref_img: Union[str, pyotb.core.OTBObject],
            interpolator: str = "bco",
            nodata: Union[float, int] = 0
    ) -> CommonImagerySource:
        """
        Return the source superimposed over the input image

        Args:
            ref_img: reference image
            interpolator: interpolator (Default value = "bco")
            nodata: no data value (Default value = 0)

        Returns:
            resampled image source

        """
        superimpose = pyotb.Superimpose({
            "inm": self,
            "inr": ref_img,
            "interpolator": interpolator,
            "fv": nodata
        })
        return self.new_source(ref_img, superimpose)

    def subset(
            self,
            startx: int,
            starty: int,
            sizex: int,
            sizey: int
    ) -> CommonImagerySource:
        """
        Return a subset

        Args:
            startx: start x
            starty: start y
            sizex: size x
            sizey: size y

        Returns:
            subset

        """
        return self.new_source(
            self[startx:startx+sizex, starty:starty+sizey, :]
        )

    def clip_over_img(
            self,
            ref_img: Union[str, pyotb.core.OTBObject]
    ) -> CommonImagerySource:
        """
        Return the source clipped over the ROI specified by the input image
        extent.

        Args:
            ref_img: reference image

        Returns:
            ROI clipped source

        """
        extract_roi = pyotb.ExtractROI({
            "in": self,
            "mode": "fit",
            "mode.fit.im": ref_img
        })
        return self.new_source(ref_img, extract_roi)

    def clip_over_vec(
            self,
            ref_vec: str
    ) -> CommonImagerySource:
        """
        Return the source clipped over the ROI specified by the input vector
        extent.

        Args:
            ref_vec: reference vector data

        Returns:
            ROI clipped source

        """
        return self.new_source(pyotb.ExtractROI({
            "in": self,
            "mode": "fit",
            "mode.fit.vect": ref_vec
        }))

    def reproject(
            self,
            epsg: int,
            interpolator: str = "bco"
    ) -> CommonImagerySource:
        """
        Reproject the source into the specified EPSG

        Args:
            epsg: EPSG (int)
            interpolator: interpolator (Default value = "bco")

        Returns:
            reprojected source

        """
        if self.root_scene.epsg != epsg:
            return self.new_source(
                pyotb.OrthoRectification({
                    "io.in": self,
                    "map": "epsg",
                    "map.epsg.code": epsg,
                    "interpolator": interpolator
                }),
                output_parameter_key='io.out'
            )
        return self  # Nothing but a soft copy of the source

    def cached(
            self,
            temporary_directory: str = None,
            extension: str = None,
            pixel_type: str = None,
            summary_modifier: Callable = None
    ) -> CommonImagerySource:
        """
        Return the source cached.

        Args:
            temporary_directory: a temporary directory for the cached files.
                Default is system temp directory.
            extension: file extension (default: .tif)
            pixel_type: pixel type
            summary_modifier: summary modifier

        Returns:
            Cached source

        """
        return self.new_source(
            Cache(
                pyotb_output=self,
                temporary_directory=temporary_directory,
                extension=extension,
                pixel_type=pixel_type,
                summary_modifier=summary_modifier
            ))

cached(temporary_directory=None, extension=None, pixel_type=None, summary_modifier=None)

Return the source cached.

Parameters:

Name Type Description Default
temporary_directory str

a temporary directory for the cached files. Default is system temp directory.

None
extension str

file extension (default: .tif)

None
pixel_type str

pixel type

None
summary_modifier Callable

summary modifier

None

Returns:

Type Description
CommonImagerySource

Cached source

Source code in scenes/core.py
def cached(
        self,
        temporary_directory: str = None,
        extension: str = None,
        pixel_type: str = None,
        summary_modifier: Callable = None
) -> CommonImagerySource:
    """
    Return the source cached.

    Args:
        temporary_directory: a temporary directory for the cached files.
            Default is system temp directory.
        extension: file extension (default: .tif)
        pixel_type: pixel type
        summary_modifier: summary modifier

    Returns:
        Cached source

    """
    return self.new_source(
        Cache(
            pyotb_output=self,
            temporary_directory=temporary_directory,
            extension=extension,
            pixel_type=pixel_type,
            summary_modifier=summary_modifier
        ))

clip_over_img(ref_img)

Return the source clipped over the ROI specified by the input image extent.

Parameters:

Name Type Description Default
ref_img Union[str, OTBObject]

reference image

required

Returns:

Type Description
CommonImagerySource

ROI clipped source

Source code in scenes/core.py
def clip_over_img(
        self,
        ref_img: Union[str, pyotb.core.OTBObject]
) -> CommonImagerySource:
    """
    Return the source clipped over the ROI specified by the input image
    extent.

    Args:
        ref_img: reference image

    Returns:
        ROI clipped source

    """
    extract_roi = pyotb.ExtractROI({
        "in": self,
        "mode": "fit",
        "mode.fit.im": ref_img
    })
    return self.new_source(ref_img, extract_roi)

clip_over_vec(ref_vec)

Return the source clipped over the ROI specified by the input vector extent.

Parameters:

Name Type Description Default
ref_vec str

reference vector data

required

Returns:

Type Description
CommonImagerySource

ROI clipped source

Source code in scenes/core.py
def clip_over_vec(
        self,
        ref_vec: str
) -> CommonImagerySource:
    """
    Return the source clipped over the ROI specified by the input vector
    extent.

    Args:
        ref_vec: reference vector data

    Returns:
        ROI clipped source

    """
    return self.new_source(pyotb.ExtractROI({
        "in": self,
        "mode": "fit",
        "mode.fit.vect": ref_vec
    }))

drilled(msk_vec_file, inside=True, nodata=0)

Return the source drilled from the input vector data.

The default behavior is that the hole is made inside the polygon. This can be changed setting the "inside" parameter to False.

Parameters:

Name Type Description Default
msk_vec_file str

input vector data filename

required
inside bool

whether the drill is happening inside the polygon or outside (Default value = True)

True
nodata Union[float, int]

nodata value inside holes (Default value = 0)

0

Returns:

Type Description
CommonImagerySource

drilled source

Source code in scenes/core.py
def drilled(
        self,
        msk_vec_file: str,
        inside: bool = True,
        nodata: Union[float, int] = 0
) -> CommonImagerySource:
    """
    Return the source drilled from the input vector data.

    The default behavior is that the hole is made inside the polygon.
    This can be changed setting the "inside" parameter to False.

    Args:
        msk_vec_file: input vector data filename
        inside: whether the drill is happening inside the polygon or
            outside (Default value = True)
        nodata: nodata value inside holes (Default value = 0)

    Returns:
        drilled source

    """
    if ogr_open(msk_vec_file):
        # Vector data not empty
        rasterization = pyotb.Rasterization({
            "in": msk_vec_file,
            "im": self,
            "mode": "binary",
            "mode.binary.foreground": 0 if inside else 255,
            "background": 255 if inside else 0
        })
        return self.masked(binary_mask=rasterization, nodata=nodata)
    if inside:
        return self  # Nothing but a soft copy of the source
    return self.new_source(
        pyotb.BandMathX({"il": self, "exp": "0.0 * im1"}))

masked(binary_mask, nodata=0)

Return the source masked from an uint8 binary raster (0 or 1..255).

Pixels are set to "nodata" where the mask values are 0.

Parameters:

Name Type Description Default
binary_mask Union[str, OTBObject]

input mono-band binary raster filename

required
nodata Union[float, int]

nodata value for rejected values (Default value = 0)

0

Returns:

Type Description
CommonImagerySource

masked source

Source code in scenes/core.py
def masked(
        self,
        binary_mask: Union[str, pyotb.core.OTBObject],
        nodata: Union[float, int] = 0
) -> CommonImagerySource:
    """
    Return the source masked from an uint8 binary raster (0 or 1..255).

    Pixels are set to "nodata" where the mask values are 0.

    Args:
        binary_mask: input mono-band binary raster filename
        nodata: nodata value for rejected values (Default value = 0)

    Returns:
        masked source

    """
    manage_nodata = pyotb.ManageNoData({
        "in": self,
        "mode": "apply",
        "mode.apply.mask": binary_mask,
        "mode.apply.ndval": nodata
    })
    return self.new_source(binary_mask, manage_nodata)

reproject(epsg, interpolator='bco')

Reproject the source into the specified EPSG

Parameters:

Name Type Description Default
epsg int

EPSG (int)

required
interpolator str

interpolator (Default value = "bco")

'bco'

Returns:

Type Description
CommonImagerySource

reprojected source

Source code in scenes/core.py
def reproject(
        self,
        epsg: int,
        interpolator: str = "bco"
) -> CommonImagerySource:
    """
    Reproject the source into the specified EPSG

    Args:
        epsg: EPSG (int)
        interpolator: interpolator (Default value = "bco")

    Returns:
        reprojected source

    """
    if self.root_scene.epsg != epsg:
        return self.new_source(
            pyotb.OrthoRectification({
                "io.in": self,
                "map": "epsg",
                "map.epsg.code": epsg,
                "interpolator": interpolator
            }),
            output_parameter_key='io.out'
        )
    return self  # Nothing but a soft copy of the source

resample_over(ref_img, interpolator='bco', nodata=0)

Return the source superimposed over the input image

Parameters:

Name Type Description Default
ref_img Union[str, OTBObject]

reference image

required
interpolator str

interpolator (Default value = "bco")

'bco'
nodata Union[float, int]

no data value (Default value = 0)

0

Returns:

Type Description
CommonImagerySource

resampled image source

Source code in scenes/core.py
def resample_over(
        self,
        ref_img: Union[str, pyotb.core.OTBObject],
        interpolator: str = "bco",
        nodata: Union[float, int] = 0
) -> CommonImagerySource:
    """
    Return the source superimposed over the input image

    Args:
        ref_img: reference image
        interpolator: interpolator (Default value = "bco")
        nodata: no data value (Default value = 0)

    Returns:
        resampled image source

    """
    superimpose = pyotb.Superimpose({
        "inm": self,
        "inr": ref_img,
        "interpolator": interpolator,
        "fv": nodata
    })
    return self.new_source(ref_img, superimpose)

subset(startx, starty, sizex, sizey)

Return a subset

Parameters:

Name Type Description Default
startx int

start x

required
starty int

start y

required
sizex int

size x

required
sizey int

size y

required

Returns:

Type Description
CommonImagerySource

subset

Source code in scenes/core.py
def subset(
        self,
        startx: int,
        starty: int,
        sizex: int,
        sizey: int
) -> CommonImagerySource:
    """
    Return a subset

    Args:
        startx: start x
        starty: start y
        sizex: size x
        sizey: size y

    Returns:
        subset

    """
    return self.new_source(
        self[startx:startx+sizex, starty:starty+sizey, :]
    )

Scene

Bases: ABC

Scene class.

The class carries all the metadata and the imagery from the scene.

TODO: add something to select some source to fetch epsg and extent

Source code in scenes/core.py
class Scene(ABC):
    """
    Scene class.

    The class carries all the metadata and the imagery from the scene.
    # TODO: add something to select some source to fetch epsg and extent

    """

    def __init__(
            self,
            acquisition_date: datetime.datetime,
            epsg: int,
            extent_wgs84: List[Tuple(float, float)],
            assets_paths: Dict[str, str],
            sources: Dict[str, Callable],
            additional_metadata: Dict[str, str] = None,
    ):
        """
        Initializer.

        Args:
            acquisition_date: Acquisition date
            epsg: EPSG code
            extent_wgs84: extent in WGS84 coordinates reference system
            assets_paths: assets paths dict {source_name, source_path/url}
            sources: assets sources decorators dict {source_name, callable}
            additional_metadata: additional metadata

        """
        assert isinstance(acquisition_date, datetime.datetime), \
            "acquisition_date must be a datetime.datetime instance"
        self.acquisition_date = acquisition_date
        assert isinstance(epsg, int), "epsg must be an int"
        self.epsg = epsg
        assert len(extent_wgs84) >= 4, "extent must have at least 4 " \
                                       "coordinates"
        self.extent_wgs84 = extent_wgs84
        self.bbox_wgs84 = coord_list_to_bbox(extent_wgs84)
        assert isinstance(assets_paths, dict)
        self.assets_paths = assets_paths
        assert isinstance(sources, dict)
        for src_key, src in sources.items():
            assert callable(src), \
                f"source '{src_key}' must be a callable function!"
        self.sources = sources
        # Metadata only stores dict, lists, str, int, float, etc.
        self.metadata = {
            "acquisition_date": self.acquisition_date.isoformat(),
            "epsg": self.epsg,
            "extent_wgs84": self.extent_wgs84,
            "bounding_box_wgs84": str(self.bbox_wgs84),
            "rasterio_footprint_wgs84": self.get_rio_footprint()
        }
        self.metadata.update({"assets_paths": assets_paths.copy()})
        if additional_metadata:
            self.metadata.update(additional_metadata)

    def __repr__(self) -> str:
        """
        Enable one instance to be used with print()

        Returns:
            a string summarizing the metadata

        """
        msg = "Metadata:\n"
        msg += pprint(self.metadata)
        msg += "\n"
        msg += "Sources names: (type 'get_<name>()' to access one source)\n"
        for src_name in self.sources:
            msg += f"\t{src_name}\n"
        return msg.replace("\t", "    ")

    def get_rio_footprint(self):
        """
        Footprint as RasterIO does.

        Returns:
            a dict containing the footprint

        """
        return {
            "type": "Polygon",
            "coordinates": [self.extent_wgs84 + (self.extent_wgs84[0],)]
        }

    def __getattr__(self, name: str) -> Source:
        """
        Get a source
        Args:
            name: name of the source (e.g. to grab the "xs" source you have to
                call "get_xs()")

        """
        # Return None for all unknown attributes
        # https://stackoverflow.com/questions/50888391/pickle-of-object-...
        # ...with-getattr-method-in-python-returns-typeerror-object-no
        if name.startswith('__') and name.endswith('__'):
            raise AttributeError

        assert name.startswith(
            "get_"), f"{self.__class__} has no attribute '{name}'"

        def get_(*args, **kwargs):
            src_name = name[4:]  # remove "get_" to catch the source name
            assert src_name in self.sources, \
                f"Source {src_name} not registered. " \
                f"Available sources: {self.sources.keys()}"
            source_func = self.sources[src_name]
            return source_func(*args, **kwargs)

        return get_

    def get_asset_path(self, key: str) -> str:
        """
        Get asset path.

        Args:
            key: asset key

        Returns:
            asset path

        """
        return self.assets_paths.get(key)

__getattr__(name)

Get a source Args: name: name of the source (e.g. to grab the "xs" source you have to call "get_xs()")

Source code in scenes/core.py
def __getattr__(self, name: str) -> Source:
    """
    Get a source
    Args:
        name: name of the source (e.g. to grab the "xs" source you have to
            call "get_xs()")

    """
    # Return None for all unknown attributes
    # https://stackoverflow.com/questions/50888391/pickle-of-object-...
    # ...with-getattr-method-in-python-returns-typeerror-object-no
    if name.startswith('__') and name.endswith('__'):
        raise AttributeError

    assert name.startswith(
        "get_"), f"{self.__class__} has no attribute '{name}'"

    def get_(*args, **kwargs):
        src_name = name[4:]  # remove "get_" to catch the source name
        assert src_name in self.sources, \
            f"Source {src_name} not registered. " \
            f"Available sources: {self.sources.keys()}"
        source_func = self.sources[src_name]
        return source_func(*args, **kwargs)

    return get_

__init__(acquisition_date, epsg, extent_wgs84, assets_paths, sources, additional_metadata=None)

Initializer.

Parameters:

Name Type Description Default
acquisition_date datetime

Acquisition date

required
epsg int

EPSG code

required
extent_wgs84 List[Tuple(float, float)]

extent in WGS84 coordinates reference system

required
assets_paths Dict[str, str]

assets paths dict {source_name, source_path/url}

required
sources Dict[str, Callable]

assets sources decorators dict {source_name, callable}

required
additional_metadata Dict[str, str]

additional metadata

None
Source code in scenes/core.py
def __init__(
        self,
        acquisition_date: datetime.datetime,
        epsg: int,
        extent_wgs84: List[Tuple(float, float)],
        assets_paths: Dict[str, str],
        sources: Dict[str, Callable],
        additional_metadata: Dict[str, str] = None,
):
    """
    Initializer.

    Args:
        acquisition_date: Acquisition date
        epsg: EPSG code
        extent_wgs84: extent in WGS84 coordinates reference system
        assets_paths: assets paths dict {source_name, source_path/url}
        sources: assets sources decorators dict {source_name, callable}
        additional_metadata: additional metadata

    """
    assert isinstance(acquisition_date, datetime.datetime), \
        "acquisition_date must be a datetime.datetime instance"
    self.acquisition_date = acquisition_date
    assert isinstance(epsg, int), "epsg must be an int"
    self.epsg = epsg
    assert len(extent_wgs84) >= 4, "extent must have at least 4 " \
                                   "coordinates"
    self.extent_wgs84 = extent_wgs84
    self.bbox_wgs84 = coord_list_to_bbox(extent_wgs84)
    assert isinstance(assets_paths, dict)
    self.assets_paths = assets_paths
    assert isinstance(sources, dict)
    for src_key, src in sources.items():
        assert callable(src), \
            f"source '{src_key}' must be a callable function!"
    self.sources = sources
    # Metadata only stores dict, lists, str, int, float, etc.
    self.metadata = {
        "acquisition_date": self.acquisition_date.isoformat(),
        "epsg": self.epsg,
        "extent_wgs84": self.extent_wgs84,
        "bounding_box_wgs84": str(self.bbox_wgs84),
        "rasterio_footprint_wgs84": self.get_rio_footprint()
    }
    self.metadata.update({"assets_paths": assets_paths.copy()})
    if additional_metadata:
        self.metadata.update(additional_metadata)

__repr__()

Enable one instance to be used with print()

Returns:

Type Description
str

a string summarizing the metadata

Source code in scenes/core.py
def __repr__(self) -> str:
    """
    Enable one instance to be used with print()

    Returns:
        a string summarizing the metadata

    """
    msg = "Metadata:\n"
    msg += pprint(self.metadata)
    msg += "\n"
    msg += "Sources names: (type 'get_<name>()' to access one source)\n"
    for src_name in self.sources:
        msg += f"\t{src_name}\n"
    return msg.replace("\t", "    ")

get_asset_path(key)

Get asset path.

Parameters:

Name Type Description Default
key str

asset key

required

Returns:

Type Description
str

asset path

Source code in scenes/core.py
def get_asset_path(self, key: str) -> str:
    """
    Get asset path.

    Args:
        key: asset key

    Returns:
        asset path

    """
    return self.assets_paths.get(key)

get_rio_footprint()

Footprint as RasterIO does.

Returns:

Type Description

a dict containing the footprint

Source code in scenes/core.py
def get_rio_footprint(self):
    """
    Footprint as RasterIO does.

    Returns:
        a dict containing the footprint

    """
    return {
        "type": "Polygon",
        "coordinates": [self.extent_wgs84 + (self.extent_wgs84[0],)]
    }

Source

Bases: Output

Source class.

Holds common operations on image sources (e.g. drill, resample, extract an ROI, etc.) Inherits from pyotb.Output

Source code in scenes/core.py
class Source(pyotb.Output):
    """
    Source class.

    Holds common operations on image sources (e.g. drill, resample, extract an
    ROI, etc.)
    Inherits from pyotb.Output

    """

    def __init__(
            self,
            root_scene: Scene,
            out: Union[str, pyotb.core.OTBObject],
            parent: Source = None,
            output_parameter_key: str = None
    ):
        """
        Args:
            root_scene: root scene
            out: image to deliver (can be an image filename (str), a pyotb.App,
                etc.)
            parent: parent Source instance
            output_parameter_key: output parameter key of the app

        """
        assert isinstance(root_scene, Scene), \
            f"root_scene type is {type(root_scene)}"
        self.root_scene = root_scene
        # Here we call the pyotb.Output() constructor.
        # Since it can only be called with pyotb apps, we do the following:
        # - if the output is a str, (e.g. the original dimap filename), we
        #   instantiate a pyotb.Input(),
        # - else we use the original output (should be pyotb application)
        app = out  # Fine for all otbApplication, pyotb.App based classes
        if isinstance(out, str):
            app = pyotb.Input(out)
        elif isinstance(out, pyotb.Output):
            app = out.parent_pyotb_app

        super().__init__(
            pyotb_app=app,
            param_key=output_parameter_key or app.output_image_key
        )
        assert parent is not self, "You cannot assign a new source to its " \
                                   "parent instance"
        self.parent = parent  # parent source (is another Source instance)
        self._app_stack = []  # list of otb applications or output to keep

    def new_source(self, *args, **kwargs) -> Source:
        """
        Return a new Source instance with new apps added at the end of the
        pipeline.

        Args:
            *args: list of pyotb.app instances to append to the existing
                pipeline
            **kwargs: some keyword arguments for Source instantiation

        Returns:
            new source

        """
        for new_app in args:
            self._app_stack.append(new_app)
        return self.__class__(
            root_scene=self.root_scene,
            out=self._app_stack[-1],
            parent=self, **kwargs
        )

    def __getattr__(self, name: str):
        """
        This function is called when an attribute or a method has been called,
        but not found in the object properties.
        We override it to avoid falling back into the depths of
        `pyotb.OTBObject`.

        Args:
            name: name of the attribute or method to access

        """
        try:
            super().__getattr__(name)
        except Exception as err:
            raise AttributeError(
                f"There is no such attribute as '{name}' here!") from err

    def __str__(self) -> str:
        """
        Enable one instance to be used with print()

        Returns:
            a string summarizing the metadata

        """
        return pprint(pyotb.summarize(self.parent_pyotb_app))

__getattr__(name)

This function is called when an attribute or a method has been called, but not found in the object properties. We override it to avoid falling back into the depths of pyotb.OTBObject.

Parameters:

Name Type Description Default
name str

name of the attribute or method to access

required
Source code in scenes/core.py
def __getattr__(self, name: str):
    """
    This function is called when an attribute or a method has been called,
    but not found in the object properties.
    We override it to avoid falling back into the depths of
    `pyotb.OTBObject`.

    Args:
        name: name of the attribute or method to access

    """
    try:
        super().__getattr__(name)
    except Exception as err:
        raise AttributeError(
            f"There is no such attribute as '{name}' here!") from err

__init__(root_scene, out, parent=None, output_parameter_key=None)

Parameters:

Name Type Description Default
root_scene Scene

root scene

required
out Union[str, OTBObject]

image to deliver (can be an image filename (str), a pyotb.App, etc.)

required
parent Source

parent Source instance

None
output_parameter_key str

output parameter key of the app

None
Source code in scenes/core.py
def __init__(
        self,
        root_scene: Scene,
        out: Union[str, pyotb.core.OTBObject],
        parent: Source = None,
        output_parameter_key: str = None
):
    """
    Args:
        root_scene: root scene
        out: image to deliver (can be an image filename (str), a pyotb.App,
            etc.)
        parent: parent Source instance
        output_parameter_key: output parameter key of the app

    """
    assert isinstance(root_scene, Scene), \
        f"root_scene type is {type(root_scene)}"
    self.root_scene = root_scene
    # Here we call the pyotb.Output() constructor.
    # Since it can only be called with pyotb apps, we do the following:
    # - if the output is a str, (e.g. the original dimap filename), we
    #   instantiate a pyotb.Input(),
    # - else we use the original output (should be pyotb application)
    app = out  # Fine for all otbApplication, pyotb.App based classes
    if isinstance(out, str):
        app = pyotb.Input(out)
    elif isinstance(out, pyotb.Output):
        app = out.parent_pyotb_app

    super().__init__(
        pyotb_app=app,
        param_key=output_parameter_key or app.output_image_key
    )
    assert parent is not self, "You cannot assign a new source to its " \
                               "parent instance"
    self.parent = parent  # parent source (is another Source instance)
    self._app_stack = []  # list of otb applications or output to keep

__str__()

Enable one instance to be used with print()

Returns:

Type Description
str

a string summarizing the metadata

Source code in scenes/core.py
def __str__(self) -> str:
    """
    Enable one instance to be used with print()

    Returns:
        a string summarizing the metadata

    """
    return pprint(pyotb.summarize(self.parent_pyotb_app))

new_source(*args, **kwargs)

Return a new Source instance with new apps added at the end of the pipeline.

Parameters:

Name Type Description Default
*args

list of pyotb.app instances to append to the existing pipeline

()
**kwargs

some keyword arguments for Source instantiation

{}

Returns:

Type Description
Source

new source

Source code in scenes/core.py
def new_source(self, *args, **kwargs) -> Source:
    """
    Return a new Source instance with new apps added at the end of the
    pipeline.

    Args:
        *args: list of pyotb.app instances to append to the existing
            pipeline
        **kwargs: some keyword arguments for Source instantiation

    Returns:
        new source

    """
    for new_app in args:
        self._app_stack.append(new_app)
    return self.__class__(
        root_scene=self.root_scene,
        out=self._app_stack[-1],
        parent=self, **kwargs
    )

load_scenes(pickle_file)

Use pickle to load scenes

Parameters:

Name Type Description Default
pickle_file str

pickle file

required

Returns:

Type Description
List[Scene]

list of Scene instances

Source code in scenes/core.py
def load_scenes(pickle_file: str) -> List[Scene]:
    """
    Use pickle to load scenes

    Args:
        pickle_file: pickle file

    Returns:
        list of Scene instances

    """
    with open(pickle_file, "rb") as file:
        return pickle.load(file)

save_scenes(scenes_list, pickle_file)

Use pickle to save scenes

Parameters:

Name Type Description Default
scenes_list List[Scene]

a list of Scene instances

required
pickle_file str

pickle file

required
Source code in scenes/core.py
def save_scenes(scenes_list: List[Scene], pickle_file: str):
    """
    Use pickle to save scenes

    Args:
        scenes_list: a list of Scene instances
        pickle_file: pickle file

    """
    with open(pickle_file, "wb") as file:
        pickle.dump(scenes_list, file)