Quick start
The example below, running in the Mappia calculator - click it to run it live. Full-size picture
- Run it
Click the picture: the map opens right here and runs the example. Nothing is saved, and nothing to install.
- 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.
- 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.
- More ways to use it
Key parameters
| Parameter | Example | What it does |
|---|---|---|
expression | function (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. |
beforeCalc | function (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. |
legendColor | function (color, inputs, lastValue, value) { return [0, value * 255 / 1830, 0]; } | Returns the [R, G, B] colour of each value of the automatic legend. |
afterCalc | function (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";namelists the maps it reads. expressionruns in a background worker, once per pixel. It sees only its arguments andthis: no variables from the code around it, nowindow, no page, no map.layersVals[i]is the value of the i-th map ofname. Withname: "CSR:geologia,CSR:altimetria",layersVals[0]is geology andlayersVals[1]elevation.inputsis a plain array of the widget values, in the order the widgets appear indescriptionHtml:inputs[0]is the first.inputs.id[ID]does not exist here; it works inbeforeCalc,afterCalc,onInputsReadyandfunctions.- Return the pixel’s value, or
this.nullValuefor no data (a transparent pixel). - Write it as a regular
function:this.isNumeric(v)andthis.nullValuecome fromthis.
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;valueis the highest value that maps to the entry; coloris an[R, G, B]array. A CSS string such as"#c0392b"throwscolor.join is not a function;- add
{{legendhtml}}todescriptionHtmlto 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
operationleaves the layer empty; the console saysOperation X is not defined for layer Y. - A value that is
undefinedonly insideexpressioncame from a variable, the page or a global: compute it inbeforeCalcand pass it as an input. - More traps are listed in pitfalls.