A metatask is a "task of tasks," an ENVITask that contains two or more ENVITasks linked together to process data. Creating a metatask provides a way to coordinate complex processes by combining several tasks. It consists of a .task file that you can share with others or run in an enterprise environment using ENVI Server.

This topic provides a simple example of a metatask that includes ISODATA classification and post-classification smoothing. The example will show how you can display a dynamic user interface for entering input and output parameters, as follows:

See the following sections:

Components of a Metatask


A metatask is essentially an ENVITask whose base_class key is set to ENVIMETATASK, for example:

{
  "name": "ISODATAClassificationWithSmoothing",
  "base_class": "ENVIMETATASK",
  "display_name": "ISODATA Classification with Smoothing",
  "description": "This task performs ISODATA unsupervised classification, followed by post-classification smoothing.",
  "schema": "envitask_3.2",

Next are the parameters, which include input parameters that the end-user provides, along with references to output parameters that the metatask will create upon execution. The input parameters in this example are INPUT_RASTER, NUMBER_OF_CLASSES, KERNEL_SIZE, and OUTPUT_RASTER_URI. The OUTPUT_RASTER is an output parameter.

In this example, we have chosen to hide additional ISODATA classification parameters to simplify its usage. These are CHANGE_THRESHOLD and ITERATIONS.

"parameters": [
  {
   "name": "INPUT_RASTER",
   "type": "ENVIRASTER",
   "direction": "INPUT",
   "required": true,
   "hidden": false,
   "description": "Specify a raster on which to perform unsupervised classification.",
   "display_name": "Input Raster",
   "keyword": "TARGET"
  },
  {
   "name": "NUMBER_OF_CLASSES",
   "type": "UINT",
   "direction": "INPUT",
   "required": false,
   "hidden": false,
   "default": 5,
   "description": "The requested number of classes to generate.",
   "display_name": "Number Of Classes",
   "keyword": "NUMBER_CLASSES",
   "test_overflow": 1,
   "test_exact": 0
  },
  {
   "name": "KERNEL_SIZE",
   "type": "UINT",
   "direction": "INPUT",
   "required": false,
   "hidden": false,
   "default": 3,
   "description": "The smooth kernel size, using an odd number (e.g., 3 = 3x3 pixels).",
   "display_name": "Kernel Size",
   "test_overflow": 1,
   "test_exact": 0
  },
  {
   "name": "OUTPUT_RASTER",
   "type": "ENVIRASTER",
   "direction": "OUTPUT",
   "required": true,
   "hidden": false,
   "description": "This is a reference to the output classification raster of filetype ENVI.",
   "display_name": "Output Raster",
   "uri_param": "OUTPUT_RASTER_URI",
   "keyword": "OUT_FILENAME"
  },
  {
   "name": "OUTPUT_RASTER_URI",
   "type": "ENVIURI",
   "direction": "INPUT",
   "required": false,
   "hidden": false,
   "description": "Specify a string with the fully-qualified path and filename for OUTPUT_RASTER.",
   "display_name": "Output Raster URI",
   "keyword": "OUT_FILENAME",
   "fold_case": 1,
   "is_directory": 0,
   "auto_extension": ".dat",
   "is_temporary": 1
  }
]

Mapping Scheme

Once the parameters have been defined, the next step is to define the input/output/parameter connections between the metatask and its individual tasks within. Add a new parameter with the name key set to DAG. Then set the type key to ENVIMETATASKDAG; for example:

{
    "name": "DAG",
    "display_name": "DAG",
    "type": "ENVIMETATASKDAG",
    "direction": "INPUT",
    "required": true,
    "description": "This is the graph that describes the metatask.",
    "hidden": true,
    "default": {

Note: DAG stands for directed acyclic graph, which is a mathematical term for a graph of nodes and edges, where each edge has a single direction and no loops exist. We use this concept in the metatask definition to specify the connections between the tasks that comprise the metatask. Nodes represent child tasks, and the directed edges are dependencies between the tasks, where the output of the independent task is an input to the dependent task.

Within the elements code block, connect (or map) parameters to one another using the following keys:

  • external_input: Use this key to connect parameters of individual tasks to parameters at the metatask level. These are the parameters that an end-user would enter via a dynamic user interface or at the IDL command line.
  • internal_input: Use this key to connect parameters from one task to another (inside of the metatask).
  • static_input: Use this key to define a fixed value for a parameter, something that cannot be changed before executing the metatask.

The following diagram shows the example classification metatask that consists of two individual tasks: ISODATAClassification and ClassificationSmoothing.

The definition for the ISODATA classification task has two external inputs, input_raster and number_of_classes. It has one static input, iterations.

The definition for the Classication Smoothing task has:

  • Two external inputs, kernel_size and output_raster_uri
  • One internal input, which is connected to the output_raster from ISODATA Classification
  • One output parameter, output_raster, which is the final output raster for the metatask

 

Run the Metatask


Follow these steps to run the metatask:

  1. Start IDL.
  2. See the Full Example in JSON Format below. Copy this code into the IDL Editor.
  3. Save the file as ISODATAClassificationWithSmoothing.task in the custom_code directory of your ENVI installation:
    • Windows: C:\Program Files\INSTALL_DIR\ENVIxx\custom_code
    • Linux: /usr/local/INSTALL_DIR/envixx/custom_code
  4. Copy and paste the following code into the IDL command line:
; Start the application
e = ENVI()
 
; Select an input file
file = FILEPATH('qb_boulder_msi', $
  SUBDIR=['data'], ROOT_DIR=e.Root_Dir)
raster = e.OpenRaster(file)
 
; Get the task from the catalog of ENVITasks
task = ENVITask('ISODATAClassificationWithSmoothing')
 
; Define input parameters
task.INPUT_RASTER = raster
task.NUMBER_OF_CLASSES = 6
 
; Run the task
task.Execute
 
; Add the result to the Data Manager
dataColl = e.Data
dataColl.Add, task.OUTPUT_RASTER
 
; Display the result
view = e.GetView()
layer = view.CreateLayer(task.OUTPUT_RASTER)
view.Zoom, /FULL_EXTENT

Or, use the following code to enter the input and output parameters using a dynamic user interface:

; Start the application
e = ENVI()
 
; Select an input file
file = FILEPATH('qb_boulder_msi', $
  SUBDIR=['data'], ROOT_DIR=e.Root_Dir)
raster = e.OpenRaster(file)
 
; Get the task from the catalog of ENVITasks
task = ENVITask('ISODATAClassificationWithSmoothing')
 
; Display the dynamic UI
result = e.UI.SelectTaskParameters(task)
 
; Run the task
task.Execute
 
; Add the result to the Data Manager
dataColl = e.Data
dataColl.Add, task.OUTPUT_RASTER
 
; Display the result
view = e.GetView()
layer = view.CreateLayer(task.OUTPUT_RASTER)
view.Zoom, /FULL_EXTENT

Full Example in JSON Format


{
  "name": "ISODATAClassificationWithSmoothing",
  "base_class": "ENVIMETATASK",
  "display_name": "ISODATA Classification with Smoothing",
  "description": "This task performs ISODATA unsupervised classification, followed by post-classification smoothing.",
  "schema": "envitask_3.2",
  "parameters": [
    {
     "name": "INPUT_RASTER",
     "type": "ENVIRASTER",
     "direction": "INPUT",
     "required": true,
     "hidden": false,
     "description": "Specify a raster on which to perform unsupervised classification.",
     "display_name": "Input Raster",
     "keyword": "TARGET"
    },
    {
     "name": "NUMBER_OF_CLASSES",
     "type": "UINT",
     "direction": "INPUT",
     "required": false,
     "hidden": false,
     "default": 5,
     "description": "The requested number of classes to generate.",
     "display_name": "Number Of Classes",
     "keyword": "NUMBER_CLASSES",
     "test_overflow": 1,
     "test_exact": 0
    },
    {
     "name": "KERNEL_SIZE",
     "type": "UINT",
     "direction": "INPUT",
     "required": false,
     "hidden": false,
     "default": 3,
     "description": "The smooth kernel size, using an odd number (e.g., 3 = 3x3 pixels).",
     "display_name": "Kernel Size",
     "test_overflow": 1,
     "test_exact": 0
    },
    {
     "name": "OUTPUT_RASTER",
     "type": "ENVIRASTER",
     "direction": "OUTPUT",
     "required": true,
     "hidden": false,
     "description": "This is a reference to the output classification raster of filetype ENVI.",
     "display_name": "Output Raster",
     "uri_param""OUTPUT_RASTER_URI",
     "keyword": "OUT_FILENAME"
    },
    {
     "name": "OUTPUT_RASTER_URI",
     "type": "ENVIURI",
     "direction": "INPUT",
     "required": false,
     "hidden": false,
     "description": "Specify a string with the fully-qualified path and filename for OUTPUT_RASTER.",
     "display_name": "Output Raster URI",
     "keyword": "OUT_FILENAME",
     "fold_case": 1,
     "is_directory": 0,
     "auto_extension": ".dat",
     "is_temporary": 1
    },
    {
      "name": "DAG",
      "display_name": "DAG",
      "type": "ENVIMETATASKDAG",
      "direction": "INPUT",
      "required": true,
      "description": "This is the graph that describes the metatask.",
      "hidden": true,
      "default": {
        "ISODATAClassification": {
          "name": "ISODATAClassification",
          "external_input": {
            "input_raster": "INPUT_RASTER",
            "number_of_classes": "NUMBER_OF_CLASSES"
          },
          "static_input": {
            "iterations" : 5
          }
        },
        "ClassificationSmoothing" : {
          "name": "ClassificationSmoothing",
          "external_input": {
            "kernel_size": "KERNEL_SIZE",
            "output_raster_uri": "OUTPUT_RASTER_URI"
          },
          "internal_input": {
            "input_raster": "ISODATAClassification.OUTPUT_RASTER"
          },
          "output": {
            "output_raster": "OUTPUT_RASTER"
          }
        }
      }
    }
  ]
}