Source code for descarteslabs.catalog.band

# Copyright 2018-2023 Descartes Labs.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from collections.abc import Iterable, Mapping, MutableMapping

from strenum import StrEnum

from ..common.collection import Collection
from ..common.property_filtering import Properties
from .catalog_base import _new_abstract_class
from .named_catalog_base import NamedCatalogObject
from .attributes import (
    Attribute,
    EnumAttribute,
    Resolution,
    BooleanAttribute,
    ListAttribute,
    TupleAttribute,
    TypedAttribute,
    ModelAttribute,
    MappingAttribute,
    AttributeValidationError,
)


properties = Properties()


[docs]class DataType(StrEnum): """Valid data types for bands. Attributes ---------- BYTE : enum An 8 bit unsigned integer value. UINT16 : enum A 16 bit unsigned integer value. INT16 : enum A 16 bit signed integer value. UINT32 : enum A 32 bit unsigned integer value. INT32 : enum A 32 bit signed integer value. FLOAT32 : enum A 32 bit single-precision floating-point format value. FLOAT64 : enum A 64 bit double-precision floating-point format value. """ BYTE = "Byte" UINT16 = "UInt16" INT16 = "Int16" UINT32 = "UInt32" INT32 = "Int32" FLOAT32 = "Float32" FLOAT64 = "Float64"
[docs]class BandType(StrEnum): """Types of bands with different data interpretation. The type of band is represented in the specific Band class being used and is only for informative purposes. Attributes ---------- CLASS : enum A band that maps a finite set of values that may not be continuous. SPECTRAL : enum A band that lies somewhere on the visible/NIR/SWIR electro-optical wavelength spectrum. MASK : enum A binary band where by convention a 0 means masked and 1 means non-masked. MICROWAVE : enum A band that lies in the microwave spectrum, often from SAR or passive radar sensors. GENERIC : enum An unspecified kind of band not fitting any other type. """ CLASS = "class" SPECTRAL = "spectral" MASK = "mask" MICROWAVE = "microwave" GENERIC = "generic"
[docs]class Colormap(StrEnum): """Predefined colormaps available to assign to bands. Most of these colormaps correspond directly to the built-in colormaps of the same name in matplotlib. See https://matplotlib.org/3.1.0/tutorials/colors/colormaps.html for an overview and visual examples. Attributes ---------- MAGMA : enum A perceptually uniform sequential colormap, equivalent to matplotlib's built-in "magma" INFERNO : enum A perceptually uniform sequential colormap, equivalent to matplotlib's built-in "inferno" PLASMA : enum A perceptually uniform sequential colormap, equivalent to matplotlib's built-in "plasma" VIRIDIS : enum A perceptually uniform sequential colormap, equivalent to matplotlib's built-in "viridis" COOL : enum A sequential colormap, equivalent to matplotlib's built-in "cool" HOT : enum A sequential colormap, equivalent to matplotlib's built-in "hot" COOLWARM : enum A diverging colormap, equivalent to matplotlib's built-in "coolwarm" BWR : enum A diverging colormap (blue-white-red), equivalent to matplotlib's built-in "bwr" GIST_EARTH : enum A colormap designed to represent topography and water depths together, equivalent to matplotlib's built-in "gist_earth" TERRAIN : enum A colormap designed to represent topography and water depths together, equivalent to matplotlib's built-in "terrain" CDL : enum A standard colormap used in Cropland Data Layer (CDL) products, with a distinct color for each class in such products """ MAGMA = "magma" INFERNO = "inferno" PLASMA = "plasma" VIRIDIS = "viridis" COOL = "cool" HOT = "hot" COOLWARM = "coolwarm" BWR = "bwr" GIST_EARTH = "gist_earth" TERRAIN = "terrain" CDL = "cdl"
[docs]class ProcessingStepAttribute(MappingAttribute): """Processing Levels Step. Attributes ---------- function : str Name of the processing function to apply. Required. parameter : str Name of the parameter in the image metadata containing the coefficients for the processing function. Required. index : int Optional index into the named parameter (an array) for the band. data_type : str or DataType Optional data type for pixel values in this band. data_range : tuple(float, float) Optional normal range of pixel values stored in the band. display_range : tuple(float, float) Optional normal range of pixel values stored in the band for display purposes. physical_range : tuple(float, float) Optional normal range of physical values stored in the band. physical_range_unit : str Optional unit of the physical range. """ function = TypedAttribute(str) parameter = TypedAttribute(str) index = TypedAttribute(int) data_type = EnumAttribute(DataType) data_range = TupleAttribute( min_length=2, max_length=2, coerce=True, attribute_type=float, ) display_range = TupleAttribute( min_length=2, max_length=2, coerce=True, attribute_type=float, ) physical_range = TupleAttribute( min_length=2, max_length=2, coerce=True, attribute_type=float, ) physical_range_unit = Attribute()
[docs]class ProcessingLevelsAttribute(ModelAttribute, MutableMapping): """An attribute that contains properties (key/value pairs). Can be set using a dictionary of items or any `Mapping`, or an instance of this attribute. All keys must be string and values can be string or an iterable of `ProcessingStepAttribute` items (or compatible mapping). `ProcessingLevelsAttribute` behaves similar to dictionaries. """ # this value is ONLY used for for instances of the attribute that # are attached to class definitions. It's confusing to put this # instantiation into __init__, because the value is only ever set # from AttributeMeta.__new__, after it's already been instantiated _attribute_name = None def __init__(self, value=None, validate=True, **kwargs): self._items = {} super(ProcessingLevelsAttribute, self).__init__(**kwargs) if value is not None: # we always validate, to correctly coerce the values value = { key: self.validate_key_and_value(key, val) for key, val in value.items() } self._items.update(value) def __repr__(self): return "{}{}{}".format( "{", ", ".join( [ "{}: {}".format(repr(key), repr(value)) for key, value in self._items.items() ] ), "}", ) def validate_key_and_value(self, key, value): """Validate the key and value. The key must be a string, and the value either a string or an iterable of `ProcessingStepAttribute` items or a compatible mapping. Returns a fully formed value (a string or a ListAttribute of `ProcessingStepAttribute` items) """ if not isinstance(key, str): raise AttributeValidationError( "Keys for property {} must be strings: {}".format( self._attribute_name, key ) ) if isinstance(value, str): return value elif isinstance(value, ListAttribute) and all( map(lambda x: isinstance(x, ProcessingStepAttribute), value) ): value._add_model_object(self) return value elif isinstance(value, Iterable): items = [] for v in value: if isinstance(v, ProcessingStepAttribute): items.append(v) elif isinstance(v, Mapping): try: items.append(ProcessingStepAttribute(**v)) except AttributeError as ex: raise AttributeValidationError( "The value for property {} with key {} must" " conform to a valid ProcessingStepAttribute: {}: {}".format( self._attribute_name, key, v, ex ) ) else: break else: value = ListAttribute(ProcessingStepAttribute, items=items) value._add_model_object(self) return value raise AttributeValidationError( "The value for property {} with key {} must be a string" " or an iterable of ProcessingStepAttribute: {}".format( self._attribute_name, key, value ) ) def serialize(self, value, jsonapi_format=False): """Serialize a value to a json-serializable type. See :meth:`Attribute.serialize`. """ if value is None: return None # Shallow copy for strings, deserialize ListAttributes return { k: ( v if isinstance(v, str) else v.serialize(v, jsonapi_format=jsonapi_format) ) for k, v in value.items() } def deserialize(self, value, validate=True): """Deserialize a value to a native type. See :meth:`Attribute.deserialize`. Parameters ---------- value : dict or `ProcessingLevelsAttribute` A set of values to use to initialize a new `ProcessingLevelsAttribute` instance. All keys must be strings, and values can be strings or numbers. Returns ------- ProcessingLevelsAttribute A `ProcessingLevelsAttribute` with the given items. Raises ------ AttributeValidationError If the value is not a mapping or any of the keys are not strings, or any of the values are not strings or numbers. """ if value is None: return None if isinstance(value, ProcessingLevelsAttribute): return value if validate: if not isinstance(value, Mapping): raise AttributeValidationError( "A ProcessingLevelsAttribute expects a mapping: {}".format( self._attribute_name ) ) value = { key: self.validate_key_and_value(key, val) for key, val in value.items() } return ProcessingLevelsAttribute( value, validate=validate, **self._get_attr_params() ) # Mapping methods def __getitem__(self, key): return self._items[key] def __setitem__(self, key, value): self._raise_if_immutable_or_readonly("set") value = self.validate_key_and_value(key, value) old_value = self._items.get(key, None) changed = key not in self._items or old_value != value self._set_modified(changed=changed) self._items[key] = value if isinstance(old_value, ModelAttribute): old_value._remove_model_object(self) def __delitem__(self, key): self._raise_if_immutable_or_readonly("delete") if key in self._items: self._set_modified(changed=True) old_value = self._items.pop(key) if isinstance(old_value, ModelAttribute): old_value._remove_model_object(self) def __iter__(self): return iter(self._items) def __len__(self): return len(self._items)
[docs]class DerivedParamsAttribute(MappingAttribute): """Derived Band Parameters Attribute. Attributes ---------- function : str Name of the function to apply. Required. bands : list(str) Names of the bands used as input to the function. Required. source_type : int Optional index into the named parameter (an array) for the band. """ function = TypedAttribute(str) bands = ListAttribute(TypedAttribute(str), validate=True) source_type = EnumAttribute( DataType, doc="str or DataType: The datatype for extracting pixels from the source bands.", ) def deserialize(self, value, validate=True): """Deserialize a value to a native type. See :meth:`Attribute.deserialize`. Parameters ---------- values : dict or MappingAttribute The values to use to initialize a new MappingAttribute. Returns ------- DerivedParamsAttribute A `DerivedParamsAttribute` instance with the given values. Raises ------ AttributeValidationError If the value is not a `DerivedParamsAttribute` or a mapping with a `function`, `bands`, and (optionally) `source_type key. """ result = super(DerivedParamsAttribute, self).deserialize(value, validate) if result: if not result.function: raise AttributeValidationError("'function' field required.") if not result.bands: raise AttributeValidationError("'bands' field required.") return result
[docs]class Band(NamedCatalogObject): """A data band in images of a specific product. This is an abstract class that cannot be instantiated, but can be used for searching across all types of bands. The concrete bands are represented by the derived classes. Common attributes: :attr:`~descarteslabs.catalog.GenericBand.id`, :attr:`~descarteslabs.catalog.GenericBand.name`, :attr:`~descarteslabs.catalog.GenericBand.product_id`, :attr:`~descarteslabs.catalog.GenericBand.description`, :attr:`~descarteslabs.catalog.GenericBand.type`, :attr:`~descarteslabs.catalog.GenericBand.sort_order`, :attr:`~descarteslabs.catalog.GenericBand.vendor_order`, :attr:`~descarteslabs.catalog.GenericBand.data_type`, :attr:`~descarteslabs.catalog.GenericBand.nodata`, :attr:`~descarteslabs.catalog.GenericBand.data_range`, :attr:`~descarteslabs.catalog.GenericBand.display_range`, :attr:`~descarteslabs.catalog.GenericBand.resolution`, :attr:`~descarteslabs.catalog.GenericBand.band_index`, :attr:`~descarteslabs.catalog.GenericBand.file_index`, :attr:`~descarteslabs.catalog.GenericBand.jpx_layer_index`. :attr:`~descarteslabs.catalog.GenericBand.vendor_band_name`. To create a new band instantiate one of those specialized classes: * `SpectralBand`: A band that lies somewhere on the visible/NIR/SWIR electro-optical wavelength spectrum. Specific attributes: :attr:`~SpectralBand.physical_range`, :attr:`~SpectralBand.physical_range_unit`, :attr:`~SpectralBand.wavelength_nm_center`, :attr:`~SpectralBand.wavelength_nm_min`, :attr:`~SpectralBand.wavelength_nm_max`, :attr:`~SpectralBand.wavelength_nm_fwhm`, :attr:`~SpectralBand.processing_levels`, :attr:`~SpectralBand.derived_params`. * `MicrowaveBand`: A band that lies in the microwave spectrum, often from SAR or passive radar sensors. Specific attributes: :attr:`~MicrowaveBand.frequency`, :attr:`~MicrowaveBand.bandwidth`, :attr:`~MicrowaveBand.physical_range`, :attr:`~MicrowaveBand.physical_range_unit`. * `MaskBand`: A binary band where by convention a 0 means masked and 1 means non-masked. The :attr:`~Band.data_range` and :attr:`~Band.display_range` for masks is implicitly ``[0, 1]``. Specific attributes: :attr:`~MaskBand.is_alpha`. * `ClassBand`: A band that maps a finite set of values that may not be continuous to classification categories (e.g. a land use classification). A visualization with straight pixel values is typically not useful, so commonly a :attr:`~ClassBand.colormap` is used. Specific attributes: :attr:`~ClassBand.colormap`, :attr:`~ClassBand.colormap_name`, :attr:`~ClassBand.class_labels`. * `GenericBand`: A generic type for bands that are not represented by the other band types, e.g., mapping physical values like temperature or angles. Specific attributes: :attr:`~GenericBand.colormap`, :attr:`~GenericBand.colormap_name`, :attr:`~GenericBand.physical_range`, :attr:`~GenericBand.physical_range_unit`, :attr:`~GenericBand.processing_levels`, :attr:`~GenericBand.derived_params`. """ _DOC_DESCRIPTION = """A description with further details on the band. The description can be up to 80,000 characters and is used by :py:meth:`Search.find_text`. *Searchable* """ _DOC_DATATYPE = "The data type for pixel values in this band." _DOC_DATARANGE = """The range of pixel values stored in the band. The two floats are the minimum and maximum pixel values stored in this band. """ _DOC_COLORMAPNAME = """str or Colormap, optional: Name of a predefined colormap for display purposes. The colormap is applied when this band is rastered by itself in PNG or TIFF format, including in UIs where imagery is visualized. """ _DOC_COLORMAP = """list(tuple), optional: A custom colormap for this band. A list of tuples, where each nested tuple is a 4-tuple of RGBA values to map pixels whose value is the index of the list. E.g. the colormap ``[(100, 20, 200, 255)]`` would map pixels whose value is 0 in the original band to the RGBA color defined by ``(100, 20, 200, 255)``. The number of 4-tuples provided can be up to the maximum of this band's data range. Omitted values will map to black by default. """ _DOC_PHYSICALRANGE = ( "tuple(float, float), optional: A physical range that pixel values map to." ) _doc_type = "band" _url = "/bands" _derived_type_switch = "type" _default_includes = ["product"] # _collection_type set below due to circular problems description = Attribute(doc="str, optional: " + _DOC_DESCRIPTION) type = EnumAttribute( BandType, doc="""str or BandType: The type of this band, directly corresponding to a `Band` derived class. The derived classes are `SpectralBand`, `MicrowaveBand`, `MaskBand`, `ClassBand`, and `GenericBand`. The type never needs to be set explicitly, this attribute is implied by the derived class used. The type of a band does not necessarily affect how it is rastered, it mainly conveys useful information about the data it contains. *Filterable*. """, ) sort_order = TypedAttribute( int, doc="""int, optional: A number defining the default sort order for bands within a product. If not set for newly created bands, this will default to the current maximum sort order + 1 in the product. *Sortable*. """, ) vendor_order = TypedAttribute( int, doc="""int, optional: A number defining the ordering of bands within a product as defined by the data vendor. 1-based. Used for indexing ``c6s_dlsr``. Generally only used internally by certain core products. *Sortable*. """, ) data_type = EnumAttribute(DataType, doc="str or DataType: " + _DOC_DATATYPE) nodata = Attribute( doc="""float, optional: A value representing missing data in a pixel in this band.""" ) data_range = TupleAttribute( min_length=2, max_length=2, coerce=True, attribute_type=float, doc="tuple(float, float): " + _DOC_DATARANGE, ) display_range = TupleAttribute( min_length=2, max_length=2, coerce=True, attribute_type=float, doc="""tuple(float, float): The range of pixel values for display purposes. The two floats are the minimum and maximum values indicating a default reasonable range of pixel values usd when rastering this band for display purposes. """, ) resolution = Resolution( doc="""Resolution, optional: The spatial resolution of this band. *Filterable, sortable*. """ ) band_index = TypedAttribute( int, doc="int: The 0-based index into the source data to access this band." ) file_index = Attribute( doc="""int, optional: The 0-based index into the list of source files. If there are multiple files, it maps the band index to the file index. It defaults to 0 (first file). """ ) jpx_layer_index = TypedAttribute( int, doc="""int, optional: The 0-based layer index if the source data is JPEG2000 with layers. Defaults to 0. """, ) vendor_band_name = TypedAttribute( str, doc="""str, optional: The name of the band in the source file. Some source file types require that the band be indexed by name rather than by the ``band_index``. """, ) processing_levels = ProcessingLevelsAttribute() derived_params = DerivedParamsAttribute() def __new__(cls, *args, **kwargs): return _new_abstract_class(cls, Band) def __init__(self, **kwargs): if self._derived_type_switch not in kwargs: kwargs[self._derived_type_switch] = self._derived_type super(Band, self).__init__(**kwargs)
[docs] @classmethod def search(cls, client=None, request_params=None): """A search query for all bands. Returns an instance of the :py:class:`~descarteslabs.catalog.Search` class configured for searching bands. Call this on the :py:class:`Band` base class to search all types of bands or classes :py:class:`SpectralBand`, :py:class:`MicrowaveBand`, :py:class:`MaskBand`, :py:class:`ClassBand` and :py:class:`GenericBand` to search only a specific type of band. Parameters ---------- client : :py:class:`CatalogClient` A `CatalogClient` instance to use for requests to the Descartes Labs catalog. Returns ------- :py:class:`~descarteslabs.catalog.Search` An instance of the :py:class:`~descarteslabs.catalog.Search` class """ search = super(Band, cls).search(client, request_params=request_params) if cls._derived_type: search = search.filter(properties.type == cls._derived_type) return search
[docs]class BandCollection(Collection): _item_type = Band
# set here due to circular references Band._collection_type = BandCollection
[docs]class SpectralBand(Band): """A band that lies somewhere on the visible/NIR/SWIR electro-optical wavelength spectrum. Instantiating a spectral band indicates that you want to create a *new* Descartes Labs catalog spectral band. If you instead want to retrieve an existing catalog spectral band use `Band.get() <descarteslabs.catalog.Band.get>`, or if you're not sure use `SpectralBand.get_or_create() <descarteslabs.catalog.SpectralBand.get_or_create>`. You can also use `Band.search() <descarteslabs.catalog.Band.search>`. Also see the example for :py:meth:`~descarteslabs.catalog.Band.save`. Parameters ---------- client : CatalogClient, optional A `CatalogClient` instance to use for requests to the Descartes Labs catalog. The :py:meth:`~descarteslabs.catalog.CatalogClient.get_default_client` will be used if not set. kwargs : dict, optional With the exception of readonly attributes (`created`, `modified`) and with the exception of properties (`ATTRIBUTES`, `is_modified`, and `state`), any attribute listed below can also be used as a keyword argument. Also see `~SpectralBand.ATTRIBUTES`. """ _derived_type = BandType.SPECTRAL.value physical_range = TupleAttribute( min_length=2, max_length=2, coerce=True, attribute_type=float, doc=Band._DOC_PHYSICALRANGE, ) physical_range_unit = Attribute(doc="str, optional: Unit of the physical range.") wavelength_nm_center = Attribute( doc="""float, optional: Weighted center of min/max responsiveness of the band, in nm. *Filterable, sortable*. """ ) wavelength_nm_min = Attribute( doc="""float, optional: Minimum wavelength this band is sensitive to, in nm. *Filterable, sortable*. """ ) wavelength_nm_max = Attribute( doc="""float, optional: Maximum wavelength this band is sensitive to, in nm. *Filterable, sortable*. """ ) wavelength_nm_fwhm = Attribute( doc="""float, optional: Full width at half maximum value of the wavelength spread, in nm. *Filterable, sortable*. """ )
[docs]class MicrowaveBand(Band): """A band that lies in the microwave spectrum, often from SAR or passive radar sensors. Instantiating a microwave band indicates that you want to create a *new* Descartes Labs catalog microwave band. If you instead want to retrieve an existing catalog microwave band use `Band.get() <descarteslabs.catalog.Band.get>`, or if you're not sure use `MicrowaveBand.get_or_create() <descarteslabs.catalog.MicrowaveBand.get_or_create>`. You can also use `Band.search() <descarteslabs.catalog.Band.search>`. Also see the example for :py:meth:`~descarteslabs.catalog.Band.save`. Parameters ---------- client : CatalogClient, optional A `CatalogClient` instance to use for requests to the Descartes Labs catalog. The :py:meth:`~descarteslabs.catalog.CatalogClient.get_default_client` will be used if not set. kwargs : dict, optional With the exception of readonly attributes (`created`, `modified`) and with the exception of properties (`ATTRIBUTES`, `is_modified`, and `state`), any attribute listed below can also be used as a keyword argument. Also see `~MicrowaveBand.ATTRIBUTES`. """ _derived_type = BandType.MICROWAVE.value frequency = Attribute( doc="""float, optional: Center frequency of the observed microwave in GHz. *Filterable, sortable*. """ ) bandwidth = Attribute( doc="""float, optional: Chirp bandwidth of the sensor in MHz. *Filterable, sortable*. """ ) physical_range = TupleAttribute( min_length=2, max_length=2, coerce=True, attribute_type=float, doc=Band._DOC_PHYSICALRANGE, ) physical_range_unit = Attribute(doc="str, optional: Unit of the physical range.")
[docs]class MaskBand(Band): """A binary band where by convention a 0 means masked and 1 means non-masked. The `data_range` and `display_range` for masks is implicitly ``(0, 1)``. Instantiating a mask band indicates that you want to create a *new* Descartes Labs catalog mask band. If you instead want to retrieve an existing catalog mask band use `Band.get() <descarteslabs.catalog.Band.get>`, or if you're not sure use `MaskBand.get_or_create() <descarteslabs.catalog.MaskBand.get_or_create>`. You can also use `Band.search() <descarteslabs.catalog.Band.search>`. Also see the example for :py:meth:`~descarteslabs.catalog.Band.save`. Parameters ---------- client : CatalogClient, optional A `CatalogClient` instance to use for requests to the Descartes Labs catalog. The :py:meth:`~descarteslabs.catalog.CatalogClient.get_default_client` will be used if not set. kwargs : dict, optional With the exception of readonly attributes (`created`, `modified`) and with the exception of properties (`ATTRIBUTES`, `is_modified`, and `state`), any attribute listed below can also be used as a keyword argument. Also see `~MaskBand.ATTRIBUTES`. """ _derived_type = BandType.MASK.value is_alpha = BooleanAttribute( doc="""bool, optional: Whether this band should be useable as an alpha band during rastering. This enables special behavior for this band during rastering. If this is ``True`` and the band appears as the last band in a raster operation (such as :meth:`descarteslabs.catalog.imagecollection.ImageCollection.mosaic` or :meth:`descarteslabs.catalog.imagecollection.ImageCollection.stack`) pixels with a value of 0 in this band will be treated as transparent. """ ) data_range = Attribute(mutable=False, doc="tuple(float, float), readonly: [0, 1].") display_range = Attribute( mutable=False, doc="tuple(float, float), readonly: [0, 1]." )
[docs]class ClassBand(Band): """A band that maps a finite set of values that may not be continuous. For example land use classification. A visualization with straight pixel values is typically not useful, so commonly a colormap is used. Instantiating a class band indicates that you want to create a *new* Descartes Labs catalog class band. If you instead want to retrieve an existing catalog class band use `Band.get() <descarteslabs.catalog.Band.get>`, or if you're not sure use `ClassBand.get_or_create() <descarteslabs.catalog.ClassBand.get_or_create>`. You can also use `Band.search() <descarteslabs.catalog.Band.search>`. Also see the example for :py:meth:`~descarteslabs.catalog.Band.save`. Parameters ---------- client : CatalogClient, optional A `CatalogClient` instance to use for requests to the Descartes Labs catalog. The :py:meth:`~descarteslabs.catalog.CatalogClient.get_default_client` will be used if not set. kwargs : dict, optional With the exception of readonly attributes (`created`, `modified`) and with the exception of properties (`ATTRIBUTES`, `is_modified`, and `state`), any attribute listed below can also be used as a keyword argument. Also see `~ClassBand.ATTRIBUTES`. """ _derived_type = BandType.CLASS.value colormap_name = EnumAttribute(Colormap, doc=Band._DOC_COLORMAPNAME) colormap = Attribute(doc=Band._DOC_COLORMAP) class_labels = ListAttribute( TypedAttribute(str), doc="""list(str or None), optional: A list of labels. A list of labels where each element is a name for the class with the value at that index. Elements can be null if there is no label at that value. """, )
[docs]class GenericBand(Band): """A generic kind of band not fitting any other type. For example mapping physical values like temperature or angles. Instantiating a generic band indicates that you want to create a *new* Descartes Labs catalog generic band. If you instead want to retrieve an existing catalog generic band use `Band.get() <descarteslabs.catalog.Band.get>`, or if you're not sure use `GenericBand.get_or_create() <descarteslabs.catalog.GenericBand.get_or_create>`. You can also use `Band.search() <descarteslabs.catalog.Band.search>`. Also see the example for :py:meth:`~descarteslabs.catalog.Band.save`. Parameters ---------- client : CatalogClient, optional A `CatalogClient` instance to use for requests to the Descartes Labs catalog. The :py:meth:`~descarteslabs.catalog.CatalogClient.get_default_client` will be used if not set. kwargs : dict, optional With the exception of readonly attributes (`created`, `modified`) and with the exception of properties (`ATTRIBUTES`, `is_modified`, and `state`), any attribute listed below can also be used as a keyword argument. Also see `~GenericBand.ATTRIBUTES`. """ _derived_type = BandType.GENERIC.value physical_range = TupleAttribute( min_length=2, max_length=2, coerce=True, attribute_type=float, doc=Band._DOC_PHYSICALRANGE, ) physical_range_unit = Attribute(doc="str, optional: Unit of the physical range.") colormap_name = EnumAttribute(Colormap, doc=Band._DOC_COLORMAPNAME) colormap = Attribute(doc=Band._DOC_COLORMAP)