EGO Script Language

Introduction

EGO Script is a simple language used to create and edit Dinamica EGO models. The file describing the model using the EGO Script format uses the extension “.ego”.

They behave just like any XML models, so it is possible to edit, load them on the graphical interface and save them again. However, beware that the Dinamica EGO graphical interface can change the model layout and input/output format based on the currently defined options. More tips on saving EGO scripts on the GUI at the end of this language tutorial.

Overview

A simple EGO script model can be seen below:

ego1.ego
Script {{
  // Loads a GeoTiff map.
  x := LoadMap "c:/map.tif";
  // Saves an ERMapper map.
  SaveMap x "c:/map-copy.ers";
}};

The Script {{ }} is used to delimit the script text. It allows several script elements to be passed on the same file. To retain backward compatibility with previous versions, this declaration can be omitted, but this practice is not encouraged.

The second line indicates the use of a Load Map to load the Geotiff file “map.tif”. The resulting map is bound to the variable x. Note that the functor name is written without breaks. Variables are always defined and bound using the operator :=.

The third line uses a Save Map to save the map bound by variable x in a ERMapper file named “c:/map-copy.ers”.

The variables and parameters after the functor name are bound to the functor input ports. Those variables before the operator := are bound to the functor output ports.

The sequence // marks the beginning of a comment. The comment continues until the end of line.

Here is a slightly more complex example:

ego2.ego
Script {{
  landscape := LoadCategoricalMap "c:/landscape.tif";
  areas := CalcAreas landscape;
  SaveTable areas "c:/areas.csv";
}};

Note that functor Calc Areas produces a table, representing the calculated area in cells, hectares and square meters.

It is possible to omit variables representing outputs or ignore them using the variable _ (underline). So the previous example can be re-written as shown below:

ego3.ego
Script {{
  landscape := LoadCategoricalMap "c:/landscape.tif";
  _ hects := CalcAreas landscape;
  SaveLookupTable hects "c:/area_in_hectares.csv";
}};

You can also write

_ hects := CalcAreas landscape;

as

_ hects _ := CalcAreas landscape;

Note the trailing _ after hects.

It is also possible to use the input and output names to bind variables and parameters. The previous example can be rewritten as

ego4.ego
Script {{
  { landscape=map } := LoadCategoricalMap { filename="c:/landscape.tif" };
  { hects=cellAreaInHectares } := CalcAreas { categoricalMap=landscape };
  SaveLookupTable { table=hects, filename="c:/area_in_hectares.csv" };
}};

So the first line is binding the filename “c:/landscape.tif” to the input named filename.

It is possible to freely mix both styles. So you can write the previous input as

ego4.ego
Script {{
  landscape := LoadCategoricalMap { filename="c:/landscape.tif" };
  { hects=cellAreaInHectares } := CalcAreas landscape;
  SaveLookupTable { table=hects, filename="c:/area_in_hectares.csv" };
}};

It is also possible to inline a definition of functor pretty much like nesting function calls in other programming languages:

ego5.ego
Script {{
  { hects=cellAreaInHectares } := CalcAreas (LoadCategoricalMap { filename="c:/landscape.tif" });
  SaveLookupTable { table=hects, filename="c:/area_in_hectares.csv" };
}};

Note that the Load Categorical Map is now nested in the definition of Calc Areas.

Reference

Comments

You can define a single line comment preceding the text with a '//'.

// This is a single line comment.

Multi-line comments can be defined surrounding the text with '/*' and '*/'.

/* This is a multi-line
comment */

It is worth noting that there is a very important difference between both comment styles: multi-line comment style are not preserved if the model is opened using the graphical interface. Usually this is not a problem since multi-line comment can be defined using the '//' style. Ex:

// This is a multi-line
// comment

Brief and Extended Comments

The sequence === is used to separate the brief and extended comments of a functor.

// Generate the initial 'seed' 
//
// ===
// Living cells are represented by 1 and dead cells by 0 

Functor Input/Outputs

It is possible to define value to input ports using two different syntaxes:

Positional Syntax

Using this syntax you can pass values and variables based on the position of the input port. Optional input ports always came last and can be omited. However, it is not possible to omit an input and give the next one. This limitation is a huge drawback when working with function with several optional input ports.

Nominal Syntax

FIXME

Inline Syntax

FIXME

Container Functor

FIXME

Internal input port

FIXME

Internal Output Port

FIXME

Properties

Properties can be represented using two different syntaxes. The property definition is always relative to the next functor defined in the model.

The first syntax represents properties using comments.

/** name = value */

The other syntax uses the special symbol @.

@name = value

Property names must start with a letter or '_' followed by letters, '_' and '.'. Property values can include any character (including blanks). Property values including line breaks must be surrounded by '“'.

Examples of properties can be seen below:

/**
  dff.date = Wed Oct 21 18:19:21 2009
  dff.version = 1.4.0.20090812
  metadata.author = Dinamica Team
  metadata.description = Calculate the largest patch index of different categories.
  metadata.notes = "The model input is a map where the non-null values identify the patches.
 
The output is a table containing the largest patch index per category."
  metadata.organization = CSR / UFMG
  metadata.showproperties = yes
  metadata.title = Calc Largest Patch Index
  metadata.version = 1.0
*/
@alias = Largest Patch Area
@collapsed = yes

Note the declaration of a “metadata.notes” above using line breaks.

The syntax @property = value forces the expansion of the property name if it represents a special property.

Some properties are used internally by Dinamica to store special information about functors and the model script. The list of special properties can be found below:

Functor Properties

Alias Full Name Description Possible Values
alias dff.functor.alias The alias used to represent the functor in the model Any string
inline dff.functor.inline Used to indicate that functor definition must not be inlined yes/no or true/false
comment dff.functor.comment The functor comment Any string
collapsed dff.container.collapsed Used to indicate that the contained must appear closed in the graphical interface yes/no or true/false

Script Properties

Full Name Description Possible Values
dff.date The date when the model was written for the last time Any string
dff.version The version of Dinamica EGO used to update the model the last time Any string
metadata.title The model title Any string
metadata.version The model version Any string
metadata.author The list of model authors Any string
metadata.organization The list of organization of each model author Any string
metadata.description The model description Any string
metadata.keywords The list of model keywords Any string
metadata.notes The model notes Any string
metadata.showproperties Flag indicating if the property window should be displayed upon opening the model yes/no or true/false

Comments are a special property that uses a different syntax. Internally, they are expanded to their equivalent property format.

The syntax

// The input map. 
//
// Non-null values identify the patches. 

and

@comment = "The input map. 
 
Non-null values identify the patches."

are equivalent.

If used before the “Script” keyword, the // syntax is converted into a “metadata.title” and assigned to the model.

Expression Calculation Syntax

Since Dinamica EGO version 2.0, expressions in EGO Script can be represented using an additional special syntax. The example below illustrates:

if #random <= ($mean - $min) / ($max - $min) then
   $min + sqrt(#random * ($max - $min) * ($mean - $min))
else
   $mean - sqrt((1 - #random) * ($max - $min) * ($max - $min))

The symbols prefixing the variable names are called sigils. The # represents maps, $ represents values and % represents tables. 1)

The use of sigils has some advantages including making easy to determine the data type represented by the variables. They also prevent clashes between special names like “rand”, “line” and “column” and the user defined ones. 2)

Examples

The example below represents a fragment of a real model using the original syntax:

    patches := LoadCategoricalMap "../originals/landscape.ers" .no .no 0 0 .none;
    // ...
    currentCategoryId := Step step;
    // ...
    @collapsed = yes
    singleCategoryPatches := CalculateMap [
        if i1 = v1 then
            1
        else 
            null        
    ] .uint8 0 .no {{
        NumberMap patches 1;
        NumberValue currentCategoryId 1;
    }};

The model modified to use the special expression calculation syntax can be seen below:

    patches := LoadCategoricalMap "../originals/landscape.ers" .no .no 0 0 .none;
    // ...
    currentCategoryId := Step step;
    // ...
    // Isolate the patches of a single category. 
    @collapsed = yes
    singleCategoryPatches := #[ if #patches = $currenteCategoryId then 1 else null ] .uint8 0 .no;

Complex Example

@title = Calc Patch Sizes, Mean Patch Sizes and Patch Size Standard Deviations
@author = Dinamica Team
@organization = CSR / UFMG
@metaversion = 1.0
@description = Calculate the patch sizes, the mean patch sizes and the patch size standard deviation of different categories.
@notes = "The model input is a map where the non-null values identify the patches.
 
The output is a table per category containing the patch sizes and two additional tables containing the mean patch size and the patch size standard deviation per category."
@showproperties = yes
@version = 1.9.32
Script {{
    // The input map. 
    //
    // Non-null values identify the patches. 
    patches := LoadCategoricalMap "../originals/landscape.ers" .no .no 0 0 .none .none;
 
    ForEachCategory patches {{
        step = step;
 
        // Patch size standard deviations. 
        currentPatchSizeStandardDeviations := MuxLookupTable [
            "Category" "Patch_Size_Standard_Deviations_In_Hectares"
        ] updatedPatchSizeStandardDeviations;
 
        // Mean Patch sizes. 
        currentMeanPatchSizes := MuxLookupTable [
            "Category" "Mean_Patch_Sizes_In_Hectares"
        ] updatedPatchMeans;
 
        // Current category. 
        currentCategory := Step step;
 
        // Calculate the sizes of a set of patches and their corresponding mean and 
        // standard deviation. 
        @collapsed = yes
        Group {{
            // Isolate the patches of a single category. 
            @collapsed = yes
            singleCategoryIndividualizedPatches := # [
                if #patches = $currentCategory then
                    #patches
                else 
                    null        
            ] .int32 .default .no .none;
 
            // Calculate the patch size, the mean patch size and patch size standard 
            // deviation. 
            @collapsed = yes
            Group {{
                // Individualize the patches. 
                individualizedPatches := CalcPatchLabelMap {
                    source = singleCategoryIndividualizedPatches,
                    initialPatchLabel = 1,
                    onlyOrthogonalsAreAllowed = .no,
                    windowLines = 3,
                    windowColumns = 3,
                    cellType = .int32,
                    nullValue = .default,
                    patchLabelsAreSparse = .no
                };
 
                attributes := ExtractMapAttributes individualizedPatches .yes .yes;
 
                // Calculate the mean patch size. 
                //
                // The calculation uses the total patch size and the number of individual patches. 
                @collapsed = yes
                mean := $ [
                    (%attributes["cellArea"] * %attributes["nonNullCells"] / %attributes["uniqueCells"]) ? 0
                ] .no 0;
 
                // Calculate the patch sizes. 
                _ patchSizesInHectares _ := CalcAreas individualizedPatches;
 
                // Calculate the patch size standard deviation. 
                @collapsed = yes
                Group {{
                    // Calculate the patch size standard deviation. 
                    @collapsed = no
                    LogPolicy .warning .no {{
                        // Calculate the SUM(Xi - MEAN)^2, where Xi is the current patch size. 
                        @collapsed = yes
                        ForEachCategory individualizedPatches {{
                            step0 = step;
 
                            // Accumulated value of SUM(Xi - MEAN)^2. 
                            currentAccumulatedValue := MuxValue 0 updatedAccumulatedValue;
 
                            // Current patch id. 
                            currentPatchId := Step step0;
 
                            // Calculate the updated accumulated value of SUM(Xi - MEAN)^2 for the current 
                            // patch size. 
                            @collapsed = yes
                            updatedAccumulatedValue := $ [
                                $currentAccumulatedValue + ((%patchSizesInHectares[$currentPatchId] - $mean) ^ 2)
                            ] .no 0;
                        }};
                    }};
 
                    // Provide 0 as the default accumulated value even if the input map has no patches 
                    // for the current categories. 
                    accumulatedValue := ValueJunction updatedAccumulatedValue 0;
 
                    // Calculate the standard deviation using the SUM(Xi - MEAN)^2. 
                    @collapsed = yes
                    standardDeviation := $ [
                        sqrt($accumulatedValue / %attributes["uniqueCells"]) ? 0
                    ] .no 0;
                }};
 
                // Update the mean table with the mean patch size of the current category. 
                updatedPatchMeans := SetLookupTableValue currentMeanPatchSizes currentCategory mean;
 
                // Update the table of standard deviations with the standard deviation of the 
                // current category. 
                updatedPatchSizeStandardDeviations := SetLookupTableValue currentPatchSizeStandardDeviations currentCategory standardDeviation;
            }};
        }};
 
        // Save a table of patch sizes per category. 
        SaveLookupTable patchSizesInHectares "patch_sizes.csv" 2 step .none;
    }};
 
    // Save the mean patch sizes as a table. 
    SaveLookupTable updatedPatchMeans "mean_patch_sizes.csv" 2 .none .none;
 
    // Save the patch size standard deviations as a table. 
    SaveLookupTable updatedPatchSizeStandardDeviations "patch_size_standard_deviations.csv" 2 .none .none;
}};
1)
The sigils are used by some popular programming languages like Perl, PHP e Ruby Sigil_%28computer_programming%29#Language_comparison
2)
Using sigils the expression “rand + #rand” is the sum of a random value with a the value of map named “rand”