Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Note: The following examples assume that you work with SNAP 10 and the new esa_snappy interface. Relevant differences to the previous snappy interface are outlined in the text or in comment lines in the Python code snippets.

Even though SNAP is based on the Java programming language it supports also the development of operator plugins written in Python. This is made possible by the esa_snappy module. It enables Python developers to write operators for SNAP. In the background, esa_snappy uses the library jpy which builds a bridge between Java and Python.
The intention of this page is to point out where writing an operator in Python is different from writing an operator in Java and what you need to consider. In general, the concepts presented in How to create a new operatorHow to integrate an operator, and Operator Implementation Guidelines also apply here. This page will focus on pointing out where they are different.  A working example of a Python operator plugin can be found in the snap-examples repository on GitHub. The RUT project provides an a full example of a Python-based Operator in operational use (originally implemented with previous snappy, but a development branch has been updated and tested with the new esa_snappy). As you will use the Java API of SNAP Engine via esa_snappy, you should familiarize yourself with this API, or at least know where and how to look things up. 

...

In order to make the development easier, we recommend PyCharm as your IDE. Also, before starting to implement an operator you should make sure that you have followed the procedure described in Configure Python to use the new SNAP-Python (esa_snappy) interface (SNAP version 10+) for the most recent SNAP 10. (This description replaces Configure Python to use the SNAP-Python (snappy) interface (SNAP versions <= 9) for older SNAP versions.)

Project Structure

The recommended basic project structure differs slightly from the structure of a Java project. It looks as shown in the following code block.

...

Code Block
languagexml
titleinfo.xml
<operator>
    <name>org.me.MyPyOp</name>
    <alias>my_py_op</alias>
    <!-- In esa_snappy, package org.esa.snap.python is replaced by org.esa.snap.snappy -->
    <operatorClass>org.esa.snap.python.gpf.PyOperator</operatorClass>
    <version>1.0</version>
    <authors>ACME Guys</authors>
    <copyright>(C) 2016 ACME</copyright>
    <description>
        A short description of the operator
    </description>
    <namedSourceProducts>
        <sourceProduct>
            <name>source</name>
        </sourceProduct>
    </namedSourceProducts>
    <parameters>
        <parameter>
            <name>factor</name>
            <description>Short description of the parameter</description>
            <dataType>double</dataType>
            <defaultValue>1.0</defaultValue>
        </parameter>
    </parameters>
</operator>

A full example for such an XML file can be found in the example repository. In the following, the meaning and the usage of the tags are described.

XML TagDescription
<name>A unique identifier within SNAP. The identifier could be created by following the convention for the Java packages. E.g. com.companyName.domain.opName
<alias>A user-friendly alias name to be used. This name should be easily usable on the command line. So it should be short but still distinguishable. Let's say your operator computes a cloud mask using neural nets. You could name it 'NNBasedCloudMaskOperator', but this is not handy. So maybe just name it 'CompanyNameClouds' or invent some other handy name. You can use the gpt tool to get a list of already existing operators and their names.
<operatorClass>This must be always org.esa.snap.pythonsnappy.gpf.PyOperator. In esa_snappy, package org.esa.snap.python is replaced by org.esa.snap.snappy.

<version>

The version of the operator. It should follow the concept of Semantic Versioning
<authors>The names of the authors

<copyright>

The copyright notice
<description>A short description of the operator shown on the command line
<namedSourceProducts>
<sourceProduct>
<name>myInput</name>
</sourceProduct>
<sourceProduct>
<name>auxProduct</name>

</sourceProduct>

</namedSourceProducts>
This section defines one or more source products. In the GUI currently, one product is supported. On the command line, it is possible to use the names along with the -S option to specify the source product, like -SmyInput=<path>

<parameters>
    <parameter>
      <!--Parameter definiton-->
    <parameter>
<parameters>

In the parameters section, all the parameters needed by the operator are specified. Each parameter is surrounded by the <parameter> tag.

...

Like Java Operators, Python Operators need to be registered via the Java Service Provider Interface (SPI). In order to register the operator, developers need to create a file named 'org.esa.snap.pythonsnappy.gpf.PyOperatorSpi'. Again, in esa_snappy, package org.esa.snap.python is replaced by org.esa.snap.snappy. This file needs to be placed in the directory resources/META-INF/services in the project structure (see above). This file must contain the python package path and name of the operator. See below for an example and a more detailed explanation.

...


Code Block
languagejava
titleorg.esa.snap.pythonsnappy.gpf.PyOperatorSpi
# In order to publish the python operators in this plugin module to SNAP, they need to be listed in this file. On each
# line an operator class is specified by its fully qualified name.
# A fully specified name includes the Python package path and the class name both separated by dot characters ('.').
# For example:
#
# cloud_mask.CloudMaskOp
# water.mci.MciOp
# land.ndvi.NdviOp
# land_ops.FaparOp
#
# Following Python conventions, the last name in the package path must therefore either be subpackage or a Python
# file. For the 'water.mci.MciOp' entry above, the pysical representation could either be:
# (a)  water/mci.py  where mci.py defines the MciOp class
# (b)  water/mci/__init__.py  where __init__.py defines the MciOp class

ndvi_op.NdviOp

Known Important issues

...

found:

  • On Windows, for using a Python operator, e.g. S2RutOp, we need to set environment variables PYTHONHOME, PYTHONPATH to current Python installation directory.
  • Parameters for operators: for esa_snappy, primitive types (such as int) need to be represented by Java objects (such as java.lang.Integer). E.g., set in Python: Integer = jpy.get_type('java.lang.Integer'), parameters.put('targetResolution', Integer(10))  instead of parameters.put('targetResolution', 10). Again, see RUT tool development branch as example for complete implementation.