Expression

Quick start

The example below, running in the Mappia calculator - click it to run it live. Full-size picture

  1. Run it

    Click the picture: the map opens right here and runs the example. Nothing is saved, and nothing to install.

  2. Try it
    • Look at the legend in the layer panel: three elevation classes in green, yellow and brown.
    • Zoom into a mountain range: land above 1000 m is painted brown, lowlands green.
  3. Make it yours

    Copy the query, change it - the key parameters are below - and run it again in the playground, or paste it into the Mappia editor to save it as your map.

  4. More ways to use it

Key parameters

ParameterExampleWhat it does
expressionfunction (layersVals, inputs) { return layersVals[0] < 300 ? 1 : 2; }Runs once per pixel and returns the pixel’s value, or this.nullValue to leave it transparent.
beforeCalcfunction (inputs) { this.setCalculateLegend([{ color: [39, 174, 96], value: 1, title: "Low" }]); }Runs on the page before each calculation: read widgets with inputs.id[ID], set the legend, swap maps.
legendColorfunction (color, inputs, lastValue, value) { return [0, value * 255 / 1830, 0]; }Returns the [R, G, B] colour of each value of the automatic legend.
afterCalcfunction (inputs) { ExtjsUtils.ALERTIFY.log("Map updated"); }Runs on the page after each calculation; only for a layer that has an expression.

Every parameter, with its type and default, is in the reference at the end of this page.

Complete example

The query 25 lines · runs as is
// Expression: runs once per pixel, in a worker, over the values of the maps in `name`.
// Here elevation is reclassified into three classes with a legend set in beforeCalc.
[
  {
    title: "Elevation classes",
    name: "CSR:altimetria",
    source: "calculate",
    opacity: 0.85,
    visibility: true,
    paramsButtonConfig: [{ type: "query", pressed: true }],
    descriptionHtml: "{{legendhtml}}",
    beforeCalc: function () {
      this.setCalculateLegend([
        { color: [39, 174, 96], value: 1, title: "Lowlands (below 300 m)" },
        { color: [241, 196, 15], value: 2, title: "Uplands (300 - 1000 m)" },
        { color: [160, 82, 45], value: 3, title: "Highlands (above 1000 m)" },
      ]);
    },
    expression: function (layersVals) {
      var elevation = layersVals[0];
      if (!this.isNumeric(elevation)) return this.nullValue;
      return elevation < 300 ? 1 : elevation < 1000 ? 2 : 3;
    },
  },
];

More examples

Other ways to use Expression, each a complete query that runs as is - like the one above.

Two maps in one expression

Name lists both maps, and each pixel brings both values - here forest biomass, kept only where the land is low.

Click the picture to run it live. Full-size picture

The query 27 lines · runs as is
// Two maps in one expression: name lists both maps, and each pixel brings both values - here forest biomass, kept only where the land is low.
// layersVals follows the order of name: [0] the biomass (t/ha), [1] the elevation (m).
// A pixel where either map has no data is left out.
[
  {
    title: "Lowland forest biomass",
    name: "CSR:biomassa_baccini_bioma_am,CSR:altimetria",
    source: "calculate",
    opacity: 0.85,
    visibility: true,
    paramsButtonConfig: [{ type: "query", pressed: true }],
    descriptionHtml: "{{legendhtml}}",
    beforeCalc: function () {
      this.setCalculateLegend([
        { color: [22, 160, 133], value: 1, title: "Below 200 m, above 200 t/ha" },
        { color: [163, 228, 215], value: 2, title: "Below 200 m, up to 200 t/ha" },
      ]);
    },
    expression: function (layersVals) {
      var biomass = layersVals[0];
      var elevation = layersVals[1];
      if (!this.isNumeric(biomass) || !this.isNumeric(elevation)) return this.nullValue;
      if (elevation >= 200) return this.nullValue;
      return biomass > 200 ? 1 : 2;
    },
  },
];

Customize it

The rules of expression

  • The layer is source: "calculate"; name lists the maps it reads.
  • expression runs in a background worker, once per pixel. It sees only its arguments and this: no variables from the code around it, no window, no page, no map.
  • layersVals[i] is the value of the i-th map of name. With name: "CSR:geologia,CSR:altimetria", layersVals[0] is geology and layersVals[1] elevation.
  • inputs is a plain array of the widget values, in the order the widgets appear in descriptionHtml: inputs[0] is the first. inputs.id[ID] does not exist here; it works in beforeCalc, afterCalc, onInputsReady and functions.
  • Return the pixel’s value, or this.nullValue for no data (a transparent pixel).
  • Write it as a regular function: this.isNumeric(v) and this.nullValue come from this.

Where the values come from

An ordinary map gives the value of its legend entry: a number label becomes the number, a range such as “10 - 20” becomes its middle (15), and a text label such as “Floresta” stays text. Check with this.isNumeric(v) before doing arithmetic. For the original cell values, read maps published as raw maps with an operation (raw, sum, average, area…), one token per map of name; see the property catalogue.

Use a widget value

descriptionHtml: "Minimum elevation {{slider|id=min_elev|minValue=0|maxValue=2000|value=800}}",
expression: function (layersVals, inputs) {
  var elevation = layersVals[0];
  if (!this.isNumeric(elevation) || elevation < inputs[0]) return this.nullValue;
  return elevation;
},

Moving the slider recalculates the layer. Anything else the expression needs (a table, a picked feature, a value from another layer) must be turned into plain data in beforeCalc and passed as an input: an InputManager arrives as inputs[i].global.

Build the legend yourself

By default the platform builds the legend from the values the expression returns: ranges coloured from blue to red, or one entry per value with categorical: true. To fix the classes and colours, call this.setCalculateLegend(entries) in beforeCalc, as the example does:

  • sort the entries by value, ascending; value is the highest value that maps to the entry;
  • color is an [R, G, B] array. A CSS string such as "#c0392b" throws color.join is not a function;
  • add {{legendhtml}} to descriptionHtml to show the legend in the panel.

To keep the automatic classes and only choose their colours, use legendColor.

Before and after the calculation

onInputsReady(inputs) runs once, when every widget has a value. beforeCalc(inputs) runs on the page before each calculation, even without an expression. afterCalc runs after it, and only when the layer has an expression. The order and what each one may touch are in the execution model.

Pitfalls

  • A map not published with the requested operation leaves the layer empty; the console says Operation X is not defined for layer Y.
  • A value that is undefined only inside expression came from a variable, the page or a global: compute it in beforeCalc and pass it as an input.
  • More traps are listed in pitfalls.

Real maps that use it

Simple Expression Example

Simple Expression Example

Simple Expression Example

Simple layerVals Example

Simple layerVals Example

Reference: the parameters used here

Generated from the platform source. Every entry, searchable, is in the API reference; the raw data is api.json. Open an entry for its description, parameters and example; # links to it.

Layer callbacks: functions you write

LayersFunctions1 entry
Functions you write in a layer and the platform calls: expression, afterCalc and legendColor on calculated layers; beforeCalc, onInputsReady, onVisibilityChange and the functions map on calculated and file layers. Inside them this is the layer - except in expression, see Kinds of layer.

expression

function= nullcallback# This function is executed for every pixel in the map.

This function is executed for every pixel in the map. It can be used to process the information of all maps in the Layer to create a new one. This function is called regularly to update the map.

layerVals Array.<LayerValues>
Is an array that has the value associated with the current pixel for each map defined in the 'name' property. The values order is the same as the one in ‘name’. For example, if in the 'name' property we have 'name: CSR:geologia,CSR:altimetria' the layerVals[0] has the value for the 'CSR:geologia' map and the layerVals[1] has the value for the 'CSR:altimetria'.
inputs Array
The value of each input defined in the descriptionHtml. The order of the values is the same as the inputs (i.e. the first input in the descriptionHtml is inputs[0], the second is inputs[1] and so on).

Returns Value of each processed pixel.

[
  {
     title: 'Example of expression function',
     color: '#FFA500',
     elements: [
        {
           title: 'This Layer is the result of the calculation of two maps',
           name: 'CSR:geologia,CSR:altimetria',
           source: 'calculate',
           legendTitle: 'All geologys above 800m',
           visibility: true,
           expression: function(layerVals, inputs) {
              let geologyValue = layerVals[0];
              let altitudeValue = layerVals[1];

              // Hide all values lower than 800
              if (altitudeValue < 800) {
                 return undefined;
              }

              return geologyValue;
           },
        },
     ],
  },
]