How to convert virtual bands to real bands using snappy

In SNAP (GUI) I can easily right-click a virtual band and choose ”Convert band” in order to make it a “real band”. I ‘ve been searching the forum and the documentation but can’t find out how to make the same thing using snappy.

1 Like

Hi Niklas,

Sorry there is no function directly available right now.

Here is the Java code that accomplishes your task in the SNAP Desktop app: ConvertComputedBandIntoBandAction.java.

We will make the conversion code available from a single function in SNAP v5: SNAP-625.

For time being you’ll have to convert the Java code to Python yourself. The important bit is the last line, which copies the source image of the computedBand into the realBand. I’m happy to further assist you, if you encounter problems!

Cheers
Norman

Thank you @forman

Then I was on the right track. In snappy I tried to use the VirtualBand.createSourceImage() http://step.esa.int/docs/v4.0/apidoc/engine/org/esa/snap/core/datamodel/VirtualBand.html#createSourceImage--

What I really wanted to do was to “Replace NaN and infinity results by -1”. My approach was to use band math and the expression isnan('bandName')? -1 : 'bandName'. When doing that I ended up with a virtual band.

//Niklas

I am running SNAP v5, but

snappy.ProductUtils.convertComputedBandToBand

returns

AttributeErrorTraceback (most recent call last)
<ipython-input-48-2edb9646de3f> in <module>()
----> 1 snappy.ProductUtils.convertComputedBandToBand

AttributeError: type object 'org.esa.snap.core.util.ProductUtils' has no attribute 'convertComputedBandToBand'

Unfortunately the issue was wrongly marked as closed. This method is not available in version 5.
It will come with the next release.

for anyone needing this now, here’s what I came up with…

import snappy
snappy.GPF.getDefaultInstance().getOperatorSpiRegistry().loadOperatorSpis()

def convertComputedBandToBand(computedBand):

    realBand = snappy.Band(
        computedBand.getName(),
        computedBand.getDataType(),
        computedBand.getRasterWidth(),
        computedBand.getRasterHeight()
    )

    realBand.setDescription(computedBand.getDescription())
    realBand.setValidPixelExpression(computedBand.getValidPixelExpression())
    realBand.setUnit(computedBand.getUnit())
    realBand.setSpectralWavelength(computedBand.getSpectralWavelength())
    realBand.setGeophysicalNoDataValue(computedBand.getGeophysicalNoDataValue())
    realBand.setNoDataValueUsed(computedBand.isNoDataValueUsed())

    if (computedBand.isStxSet()):
        realBand.setStx(computedBand.getStx())

    imageInfo = computedBand.getImageInfo()
    if (imageInfo != None):
        realBand.setImageInfo(imageInfo.clone())

    product = computedBand.getProduct()

    # "Check if all the frame with the raster data are close"
    # missing some stuff with topComponent

    bandGroup = product.getBandGroup()
    bandIndex = bandGroup.indexOf(computedBand)
    bandGroup.remove(computedBand)
    bandGroup.add(bandIndex, realBand)

    realBand.setSourceImage(createSourceImage(computedBand, realBand))

def createSourceImage(computedBand, realBand):
    try:
        # assume computedBand is actually just a normal band
        return computedBand.getSourceImage()
    except RuntimeError as e:
        if e.message.startswith("java.lang.IllegalArgumentException"):
            # now assume computedBand is virtualBand
            virtualBand = snappy.jpy.cast(computedBand, snappy.VirtualBand)
            return snappy.VirtualBand.createSourceImage(realBand, virtualBand.getExpression())
        else:
            raise
1 Like

Hi @marpet,

Is this available in version 6?

Thanks,
Nat

Unfortunately not, It din’ make it into the release.
You still need to use use the method provided by @oak .