Type for Multiple Slave Products

I would like to know how I figure out the expected input type for tools that are run through python. Here is my code,

#Collocate
    collocate_sources=snappy.HashMap()
    collocate_sources.put('masterProduct', for_collocation[0])
    collocate_sources.put('slaveProducts', for_collocation[1:])
    collocate_params=snappy.HashMap()
    collocate_params.put('renameMasterComponents',False)
    collocate_params.put('renameSlaveComponents',False)
    collocate_params.put('resamplingType','NEAREST_NEIGHBOR')
    collocate_params.put('masterProductName',for_collocation[0].getName())
    print("collocating")
    target = GPF.createProduct('Collocate',collocate_params,collocate_sources)
    print("finished collocating")

this kind of format has worked for Resampling and Reprojecting, but the Slave input has multiple products. What is expected here? And more importantly, how would I look it up in case I run into this sort of problem again?

I’ve used the SNAP Command-Line, “gpf collocate -h” to get the names of paramters and such, but it doesn’t tell me how to feed in the sources.

Please try sourceProducts instead of slaveProducts.
You third line of code should look like:
collocate_sources.put('sourceProducts', for_collocation[1:])

Thanks for the suggestion. I tried that, but for_collocation is a python list and I get this error when I try to pass it into the HashMap,
"cannot convert a Python 'list' to a Java 'java.lang.Object'"

I’ve found a Java array example and tried this:

collocate_sources=snappy.HashMap()
collocate_sources.put('masterProduct', for_collocation[0])
slaves = jpy.array('org.esa.snap.core.datamodel.Product',8)
slaves[0]=for_collocation[1]
slaves[1]=for_collocation[2]
slaves[2]=for_collocation[3]
slaves[3]=for_collocation[4]
slaves[4]=for_collocation[5]
slaves[5]=for_collocation[6]
slaves[6]=for_collocation[7]
slaves[7]=for_collocation[8]
collocate_sources.put('sourceProducts', slaves)
collocate_params=snappy.HashMap()
collocate_params.put('renameMasterComponents',False)
collocate_params.put('renameSlaveComponents',False)
collocate_params.put('resamplingType','NEAREST_NEIGHBOR')
collocate_params.put('masterProductName',for_collocation[0].getName())
target = GPF.createProduct('Collocate',collocate_params,collocate_sources)

But then when the tool is run I get a java.lang.ClassCastException,

class [Lorg.esa.snap.core.datamodel.Product; cannot be cast to class org.esa.snap.core.datamodel.Product (they are in unnamed module of loader 'app')

I know that my product is correct by debugging,
Screenshot 2022-06-13 160702

I think I know that ‘org.esa.snap.core.datamodel.Product’ is the right type to pass the java array, since it’s what I see when I look at the Java source code here.

Am I barking up the wrong tree? I see now that GPT says ‘collocate’ is only for “two products based on their geo-codings”. Alternatively the ‘createstack’ tool “collocates two or more products based on their geo-codings.” The example I see here, from @janardanroy, seems to iteratively run the ‘createstack’ tool one at a time. When I run ‘collocate’ in SNAP the output is a single product with each slave as a band. How can I accomplish that with these tools? I’m just trying to re-create that output with Python. Do I need to collect the outputs of each iteration of ‘createstack’ and then run ‘bandmerge’? But then that leads me to a follow up question, the input for ‘bandmerge’ is string,string,string,…, how do I create that in Python?

Unfortunately, the description of the collocate tool wasn’t updated when it was extended to handle multiple sources.
So, I think it is still the right tool for you.

sources= jpy.array('org.esa.snap.core.datamodel.Product',9)
sources[0]=for_collocation[0]
sources[1]=for_collocation[1]
sources[2]=for_collocation[2]
sources[3]=for_collocation[3]
sources[4]=for_collocation[4]
sources[5]=for_collocation[5]
sources[6]=for_collocation[6]
sources[7]=for_collocation[7]
sources[8]=for_collocation[8]
collocate_params=snappy.HashMap()
collocate_params.put('renameMasterComponents',False)
collocate_params.put('renameSlaveComponents',False)
collocate_params.put('resamplingType','NEAREST_NEIGHBOR')
collocate_params.put('masterProductName', sources[0].getName())
target = GPF.createProduct('Collocate', collocate_params, sources)

Could you try this code snippet? I didn’t let it run but I think it should work.

1 Like

Thank you @marpet ! That was a long time of head scratching for me. Besides NEAREST_NEIGHBOUR being spelled with a ‘U’, it ran well! Awesome.

Would you happen to know how to keep the bands ordered when they come out of the Collocate tool? I’ve confirmed they are going into the Java array in order, but when i open the output in SNAP, the bands are random and the order changes each time I run the tool. This is confirmed by debugging before I write it to BEAM-DIMAP.

Good that it works now. The different possibilities how to configure the operator can be confusing.

They are not ordered anymore after collocation?

Actually, the order should be the same as in the sources.
The master is iterated for its bands
snap-engine/CollocateOp.java at 4611dc576fde97b5959fdbc39b13314b7b4dfa59 ¡ senbox-org/snap-engine (github.com)

And the salves too
snap-engine/CollocateOp.java at 4611dc576fde97b5959fdbc39b13314b7b4dfa59 ¡ senbox-org/snap-engine (github.com)

I will have a closer look tomorrow.

Here is a picture of the bands opened in SNAP. Ideally it will be Band1, Band2, Band3, etc.


For comparison, here is the ‘sources’ Java Array, just before it is fed to the Collocation Operator.

This is my result when doing the collocation with three products:


The flag names are a bit strange. They should follow the same pattern as for the other bands.
But the ordering is fine.

Do you have each band in a separate product? If so the order of the bands follows the order of the products. Maybe you can sort the input products?

My sources begin as individual TIFs. They are first reprojected, then resampled, then collocated. I think in my previous post I show that they are ordered correctly in the Java Array (Band1=sources[0], Band2=sources[1], etc.). Your screenshot shows that all the bands within a product are kept in order, but I’m collocating 9 different products with a single band each. Are your two slave products keeping the order that you fed them in with?

You are right. The previous image shows that they are ordered.

In my test case I used the GUI and the order of products was kept.
IN the middle you see the screenshot of the configuration in the GUI, on the left the resulting product and on the right the parameters.

The difference is that in my case the parameter ‘sourceProductPaths’ is used.
Maybe this is the difference. To you this parameter your products nee to reside on your disk.

I’ve added a ticket in our issue database for this:
[SNAP-1535] The collocation should maintain the order of the input products - JIRA (atlassian.net)

For those that want to use the Collocate tool with snappy using many slave products, this is how to do it (until the ordering of the sources is fixed)

source = jpy.array(‘org.esa.snap.core.datamodel.Product’, 1)
source[0] = ProductIO.readProduct(‘path to master’)

collocate_params = snappy.HashMap()
collocate_params.put(‘renameMasterComponents’, False)
collocate_params.put(‘renameSlaveComponents’, False)
collocate_params.put(‘copySecondaryMetadata’, False)
collocate_params.put(‘resamplingType’, ‘NEAREST_NEIGHBOUR’)
collocate_params.put(‘masterProductName’, sources[0].getName())
collocate_params.put(‘sourceProductPaths’, ‘string value of comma separated paths of slave products’)

target = GPF.createProduct(‘Collocate’, collocate_params, sources)

1 Like