stac-utils / pystac

Python library for working with any SpatioTemporal Asset Catalog (STAC)
https://pystac.readthedocs.io
Other
358 stars 120 forks source link

Band object is not iterable #1163

Closed MaxDragonheart closed 1 year ago

MaxDragonheart commented 1 year ago

Hi guys! Probably this is not a bug but my misunderstanding about how is possible to create the assets.

Whit the function below, I create a dictionary with all bands of Sentinel 2 platform:

def create_band() -> Dict:
    """Create bands for STAC Catalog.

    Returns:
        List
    """
    bands = {}
    for key, value in SENTINEL2_BANDS.items():

        name = value["name"]
        description = value["description"]
        common_name = key

        bands[name] = Band.create(
            name=name,
            description=description,
            common_name=common_name,
        )

    return bands

SENTINEL2_BANDS is a simple dictionary with values useful for me:

SENTINEL2_BANDS = {
    'coastal': {
        'name': 'B01',
        'wavelength': {
            'min': 412,
            'max': 456,
            'central': 442.45,
            'unit': 'nanometers'
        },
        'resolution': {
            'size': 60,
            'unit': 'meter',
        },
        'description': 'Coastal aerosol',
    },
    'blue': {
        'name': 'B02',
        'wavelength': {
            'min': 456,
            'max': 533,
            'central': 492.25,
            'unit': 'nanometers'
        },
        'resolution': {
            'size': 10,
            'unit': 'meter',
        },
        'description': 'Visible blue',
    },
    'green': {
        'name': 'B03',
        'wavelength': {
            'min': 538,
            'max': 583,
            'central': 559.4,
            'unit': 'nanometers'
        },
        'resolution': {
            'size': 10,
            'unit': 'meter',
        },
        'description': 'Visible green',
    },
    'red': {
        'name': 'B04',
        'wavelength': {
            'min': 646,
            'max': 684,
            'central': 664.75,
            'unit': 'nanometers'
        },
        'resolution': {
            'size': 10,
            'unit': 'meter',
        },
        'description': 'Visible red',
    },
    'rededge1': {
        'name': 'B05',
        'wavelength': {
            'min': 695,
            'max': 714,
            'central': 703.95,
            'unit': 'nanometers'
        },
        'resolution': {
            'size': 20,
            'unit': 'meter',
        },
        'description': 'Vegetation classification red edge',
    },
    'rededge2': {
        'name': 'B06',
        'wavelength': {
            'min': 731,
            'max': 749,
            'central': 739.8,
            'unit': 'nanometers'
        },
        'resolution': {
            'size': 20,
            'unit': 'meter',
        },
        'description': 'Vegetation classification red edge',
    },
    'rededge3': {
        'name': 'B07',
        'wavelength': {
            'min': 769,
            'max': 797,
            'central': 781.25,
            'unit': 'nanometers'
        },
        'resolution': {
            'size': 20,
            'unit': 'meter',
        },
        'description': 'Vegetation classification red edge',
    },
    'nir': {
        'name': 'B08',
        'wavelength': {
            'min': 760,
            'max': 907,
            'central': 832.85,
            'unit': 'nanometers'
        },
        'resolution': {
            'size': 10,
            'unit': 'meter',
        },
        'description': 'Near infrared',
    },
    'nir08': {
        'name': 'B8A',
        'wavelength': {
            'min': 837,
            'max': 881,
            'central': 864.35,
            'unit': 'nanometers'
        },
        'resolution': {
            'size': 20,
            'unit': 'meter',
        },
        'description': 'Vegetation classification red edge',
    },
    'nir09': {
        'name': 'B09',
        'wavelength': {
            'min': 932,
            'max': 958,
            'central': 944.15,
            'unit': 'nanometers'
        },
        'resolution': {
            'size': 60,
            'unit': 'meter',
        },
        'description': 'Water vapor',
    },
    'cirrus': {
        'name': 'B10',
        'wavelength': {
            'min': 1337,
            'max': 1412,
            'central': 1375.2,
            'unit': 'nanometers'
        },
        'resolution': {
            'size': 60,
            'unit': 'meter',
        },
        'description': 'Used for atmospheric correction only, not present in L2A products',
    },
    'swir16': {
        'name': 'B11',
        'wavelength': {
            'min': 1539,
            'max': 1682,
            'central': 1612.05,
            'unit': 'nanometers'
        },
        'resolution': {
            'size': 20,
            'unit': 'meter',
        },
        'description': 'Short-wave infrared, snow/ice/cloud classification',
    },
    'swir22': {
        'name': 'B12',
        'wavelength': {
            'min': 2078,
            'max': 2320,
            'central': 2194.05,
            'unit': 'nanometers'
        },
        'resolution': {
            'size': 20,
            'unit': 'meter',
        },
        'description': 'Short-wave infrared, snow/ice/cloud classification',
    },
    'aot': {
        'name': 'AOT',
        'wavelength': None,
        'resolution': {
            'size': 10,
            'unit': 'meter',
        },
        'description': 'Aerosol optical thickness',
    },
    'scl': {
        'name': 'SCL',
        'wavelength': None,
        'resolution': {
            'size': 20,
            'unit': 'meter',
        },
        'description': 'Scene classfication map',
    },
    'wvp': {
        'name': 'WVP',
        'wavelength': None,
        'resolution': {
            'size': 20,
            'unit': 'meter',
        },
        'description': 'Water vapour',
    },
    'tci': {
        'name': 'visual',
        'wavelength': None,
        'resolution': {
            'size': 20,
            'unit': 'meter',
        },
        'description': 'True color image; B04 (red), B03 (green), B02 (blue)',
    },
}

Later, I try to create the asset from each band. E.g. using B04:

    # Create asset
    asset = pystac.Asset(
        href=band_path,
        title=band_name,
        description=band_description
    )
    print(asset.to_dict())

{'href': './S2B_MSIL2A_20230601T094549_R079_T33TVF_20230601T163334/T33TVF_20230601T094549_B04_10m.tif', 'title': 'B04', 'description': 'Band description.'}

    # Get STAC band information
    band_info = create_band()[band_name]
    print(band_info)

<Band name=B04>

    # Add Electro-Optical Extension
    asset_eo_ext = EOExtension.ext(asset)
    print(asset_eo_ext)

<AssetEOExtension Asset href=./S2B_MSIL2A_20230601T094549_R079_T33TVF_20230601T163334/T33TVF_20230601T094549_B04_10m.tif>

Problems come when I try to apply band_info to asset_eo_ext:

asset_eo_ext.apply(bands=band_info)

Here the error message:

../../eo_drako/stac/stac.py:145: in sentinel2_band
    asset_eo_ext.apply(bands=band_info)
/home/max/.cache/pypoetry/virtualenvs/eo-drako-nSMVjevg-py3.10/lib/python3.10/site-packages/pystac/extensions/eo.py:311: in apply
    self.bands = bands
/home/max/.cache/pypoetry/virtualenvs/eo-drako-nSMVjevg-py3.10/lib/python3.10/site-packages/pystac/extensions/eo.py:325: in bands
    BANDS_PROP, map_opt(lambda bands: [b.to_dict() for b in bands], v)
/home/max/.cache/pypoetry/virtualenvs/eo-drako-nSMVjevg-py3.10/lib/python3.10/site-packages/pystac/utils.py:435: in map_opt
    return v if v is None else fn(v)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

bands = <Band name=B04>

>       BANDS_PROP, map_opt(lambda bands: [b.to_dict() for b in bands], v)
    )
E   TypeError: 'Band' object is not iterable

/home/max/.cache/pypoetry/virtualenvs/eo-drako-nSMVjevg-py3.10/lib/python3.10/site-packages/pystac/extensions/eo.py:325: TypeError

What I'm doing wrong?

I'm using Python 3.10 with pystac 1.7.3

gadomski commented 1 year ago

In this line:

asset_eo_ext.apply(bands=band_info)

bands should be a list[Band] -- in your case, it's just a Band. If your asset only has one band, all you need to do is wrap your band_info in a list, e.g.:

asset_eo_ext.apply(bands=[band_info])
gadomski commented 1 year ago

Closing as answered. @MaxDragonheart please re-open if that resolution is incorrect.

MaxDragonheart commented 1 year ago

Sorry for the late response. Thank for your support, this asset_eo_ext.apply(bands=[band_info]) solved the trouble :smile: