Loadcsv

Markup: {{loadcsv}}

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
    • Read the panel: the chart plots carbon loss, in gigatonnes, against the share of forest lost.
    • Hover a point of the line: the tooltip shows the carbon loss at that share.
    • Point url at your own two-column CSV and run it again: the chart redraws from your numbers.
  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.

Key parameters

ParameterExampleWhat it does
url/theme/app/data/emissoesco2.csvWhere the CSV is downloaded from; relative paths resolve on the Mappia server. Write = inside a query string as \=.
idemissionsNames the table: your callbacks read it as inputs.id["emissions"].
removeEmptyLinestrueDrops blank lines, such as a final newline, so they are not counted as data lines.
corstrueDownloads through the Mappia proxy, for servers that do not send CORS headers.
getValuetable.getValue(1, line)Reads one cell as text: column index first, then line. Line 0 is the first line after the header.
getLineCounttable.getLineCount()Number of data lines, header excluded. Loop from 0 up to it.

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

Complete example

The query 36 lines · runs as is
// LoadCsv: downloads a CSV before the layer calculates; its input value is a CSV table
// (row 0 = header, getValue(column, line) with line 0 = first data row). Drawn as a chart.
[
  {
    title: "Carbon loss by deforestation",
    name: "CSR:estados",
    source: "calculate",
    opacity: 0.35,
    visibility: true,
    paramsButtonConfig: [{ type: "query", pressed: true }],
    descriptionHtml:
      "{{loadcsv|id=emissions|url=/theme/app/data/emissoesco2.csv|removeEmptyLines=true}}" +
      '<div id="emissions_chart" style="width:290px;height:210px"></div>',
    // afterCalc runs once the expression's values are computed, so the layer needs one
    expression: function (layersVals) {
      return layersVals[0];
    },
    afterCalc: function (inputs) {
      var table = inputs.id["emissions"]; // columns: percentage, carbon_loss
      var points = [];
      for (var line = 0; line < table.getLineCount(); line++) {
        points.push([parseFloat(table.getValue(0, line)), parseFloat(table.getValue(1, line)) / 1e9]);
      }
      var old = ExtjsUtils.HIGHCHART.getById("emissions_chart");
      if (old) old.destroy();
      Highcharts.chart("emissions_chart", {
        title: { text: "Carbon loss (Gt)" },
        xAxis: { title: { text: "Forest lost (%)" } },
        yAxis: { title: { text: null } },
        legend: { enabled: false },
        credits: { enabled: false },
        series: [{ name: "Carbon loss", data: points }],
      });
    },
  },
];

Customize it

Where you can read the table

The table is available as inputs.id["emissions"] (the id you chose) in beforeCalc, onInputsReady and afterCalc, and as this.getInputs().id["emissions"] in the layer’s functions. The layer waits for the download before it calculates, so you never see a half-loaded table.

Two rules catch most people:

  • afterCalc runs only for a layer that has an expression. Without one nothing is calculated and afterCalc never runs - that is why the example has an expression that just returns layersVals[0].
  • expression runs in a worker, where inputs.id does not exist. Work with the table in the page callbacks above.

The table methods

MethodReturns
getLineCount()The number of data lines (the header is not counted)
getValue(column, line)One cell as text, trimmed. line 0 is the first data line
getLines()Every data line, each an array of cell texts
getLines(columns, values)Only the lines whose columns hold values, e.g. getLines(["state"], ["MG"])
columnNameToInd(name)The index of a header, or -1 when there is none
getColunsInd()The header row. The misspelling is the real name: getColumnsInd does not exist
createIndexes(columns)Speeds up repeated getLines calls on the same columns

Columns can be given by index or by header name. Look a value up by name like this:

afterCalc: function (inputs) {
  var table = inputs.id["prices"];
  var price = table.columnNameToInd("price");
  var lines = table.getLines(["state"], ["MG"]);   // lines whose "state" cell is "MG"
  if (price >= 0 && lines.length) {
    Ext.getCmp("price_label").setText("MG: " + lines[0][price]);
  }
},

Every cell is text

Convert with parseFloat before you calculate or compare, or "9" > "10" will surprise you. getLines compares values as text too.

Cells keep the spaces written in the file. getValue trims the cell it returns, but header names are never trimmed, so columnNameToInd needs the exact header text. In the example’s file the second header is ` carbon_loss, with a leading space - which is why the example reads its columns by index. trim=true` is accepted but does nothing.

Redraw, do not stack

afterCalc can run many times: once after every calculation round. Clear what the previous round drew before you draw again, as the example does by destroying the old chart. A chart larger than the panel fits well in a Window.

It counts as an input

{{loadcsv}} takes a position among the layer’s inputs, in markup order. If a slider comes after it, expression reads the slider as inputs[1], not inputs[0]. See the widget markup language for the full order rules.

Files on other servers

If the server does not send CORS headers, add cors=true. Inside a URL, write every = as \= (\\= inside a JavaScript string), or the markup reads it as a new parameter: {{loadcsv|id=tbl|url=https://example.com/data.csv?v\=2|cors=true}}.

Real maps that use it

Interacting Map and CSV

Interacting Map and CSV

Interacting Map and CSV

Amazon on focus - Deforestation and fires by land category

Amazon on focus - Deforestation and fires by la...

AMAZONES - Biodiversity

AMAZONES - Biodiversity

AMAZONES - Carbon stocks and CO2 emissions

AMAZONES - Carbon stocks and CO2 emissions

AMAZONES - Hydrologic services

AMAZONES - Hydrologic services

Map publishing customization

Map publishing customization

FIP Cerrado - Fire monitoring (current map)

FIP Cerrado - Fire monitoring (current map)

Panorama of Brazil's Forest Code

Panorama of Brazil's Forest Code

REDD Brazil - Deforestation, emissions and credits

REDD Brazil - Deforestation, emissions and credits

SimAmazoniaINFRA - Deforestation and CO2 scenarios

SimAmazoniaINFRA - Deforestation and CO2 scenarios

X-ray of the CAR - Forest Code balance per property

X-ray of the CAR - Forest Code balance per prop...

AreaCategorical and GetLegend.

AreaCategorical and GetLegend.

Reference: every parameter

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.

loadcsv · load a CSV table (input)

LoadCsv14 entries

Written in a layer's descriptionHtml as {{loadcsv|parameter=value|...}}

Describes the API to read and manipulate ExtjsUtils.CSV.CsvTable files from URL.
View the complete LoadCSV API here.

columnNameToInd

function(columnName) : Number# Get the index of a column with the 'columnName' name.

Get the index of a column with the 'columnName' name.

columnName String
Column name to search for.

Returns When it exists returns the column index, otherwise -1.

columnNamesToIndexes

function(columnNames) : Array.<Number># Resolves column names to column indexes: every string entry is looked up in the header (-1 when absent) and every numeric entry is kept as it is.

Resolves column names to column indexes: every string entry is looked up in the header (-1 when absent) and every numeric entry is kept as it is. A single value is accepted in place of the array. This is what getLines/createIndexes do with their columns argument.

columnNames Array.<(String|Number)>|String|Number
Column names and/or indexes.

Returns The column indexes, in the same order.

inputs.id["fire_csv"].columnNamesToIndexes(["Year", 2]); // e.g. [1, 2]

cors

Boolean= false# Downloads the CSV through the Mappia CORS proxy; use it for servers that do not send CORS headers.

Downloads the CSV through the Mappia CORS proxy; use it for servers that do not send CORS headers.

|cors=true|

createIndexes

function(columns)# Create indexes for faster search.

Create indexes for faster search.

PS: Indexes are used to faster results on "getLines" calls, apply only when all [columns] are indexes.

columns Array
(Optional) Array of indexes/names of the filtered columns.
{...
    beforeCalc: function(inputs) {
        inputs.id['CSV_WIDGET_EXAMPLE_ID'].createIndexes(['Key', 'Year']);
        alert(inputs.id['CSV_WIDGET_EXAMPLE_ID'].getLines(['Key','Year'], [100, 2020]).length);
    }
}

getColunsInd

function() : Array.<String># Returns a copy of the header row — the column names, in order — so query code can discover columns instead of hard-coding indexes.

Returns a copy of the header row — the column names, in order — so query code can discover columns instead of hard-coding indexes. The name is a historical typo of getColumnsInd, kept for compatibility (the platform and many queries call it by this spelling; there is no correctly spelled alias).

Returns Copy of the header row.

var headers = inputs.id["fire_csv"].getColunsInd(); // ["Municipality", "Year", "Fires"]
var iFires = headers.indexOf("Fires");

getLineCount

function() : Number# Number of data lines in the table (the header line is not counted).

Number of data lines in the table (the header line is not counted).

Returns How many data lines the CSV has.

var csv = inputs.id["fire_csv"];
for (var i = 0; i < csv.getLineCount(); i++) total += parseFloat(csv.getValue(2, i));

getLines

function(columns, values, includeHeader) : Array.<Array.<String>># Returns the lines (arrays of cell strings) whose cells in columns equal the corresponding entries of values.

Returns the lines (arrays of cell strings) whose cells in columns equal the corresponding entries of values. Columns may be given by index or by header name; to filter on several columns give one value per column, e.g. getLines([1, "Year"], ["Park A", 2024]). Values are compared as strings (the CSV is always text). Called with no arguments it returns every data line (header skipped) — a common idiom. When all filtered columns were indexed with createIndexes the lookup uses the index instead of scanning every line.

columns Array.<(String|Number)>
Indexes and/or names of the columns to filter on.
values Array
One value per entry of columns.
includeHeader Boolean
True to also return the header line when it matches (only in the scanning path, i.e. without indexes).

Returns The matching lines; all data lines when no filter is given.

var csv = inputs.id["fire_csv"];
var rows2024 = csv.getLines(["Year"], [2024]);      // by header name
var rowsParkA = csv.getLines([0, "Year"], ["Park A", 2024]); // two columns
var allRows = csv.getLines();                         // every data line

getValue

function(column, line, includeHeader) : String# Get a value by the matrix index and column.

Get a value by the matrix index and column.

column Number
Column index (First index is 0).
line Number
Line index (First index is 0).
includeHeader boolean
True to include the header line in the matrix index, False to ignore.

Returns Get the cell value.

id

String# Defines the id of the input; it is the key used to read the table in inputs.id[ID] (required, the tool renders nothing visible).

Defines the id of the input; it is the key used to read the table in inputs.id[ID] (required, the tool renders nothing visible).

|id=emissions_csv|

removeEmptyLines

boolean= false# Ignore the empty lines, removing them from the parsed CSV.

Ignore the empty lines, removing them from the parsed CSV. True to remove the empty lines from the CSV.

setValue

function(column, line, value)# Change a cell value by its cell index.

Change a cell value by its cell index.

column Numeric
Column index.
line Numeric
Line index to change (ignore the header information when it exists).
value *
New value to replace the older value.

trim

Boolean= false# Requests that cell values be trimmed of surrounding whitespace.

Requests that cell values be trimmed of surrounding whitespace. The flag is accepted and forwarded to the CSV parser, but the current parser ignores it (cells are stored as written, quotes removed), so trim values yourself when needed. Kept for compatibility with existing queries.

|trim=true|

url

String# Defines the URL of the CSV file to download (required).

Defines the URL of the CSV file to download (required). Relative URLs are resolved against the Mappia host, so backend endpoints such as /wmtp/calc/... work; escape = inside query strings as \=. The layer waits for the download before calculating.

|url=/theme/app/data/emissoesco2.csv|
|url=/wmtp/calc/areacategorical/?layers\=CSR:estados&styles\=1|

value

ExtjsUtils.CSV.CsvTable# Value stored in inputs.id[ID]: an ExtjsUtils.CSV.CsvTable wrapping the parsed file (first row = header).

Value stored in inputs.id[ID]: an ExtjsUtils.CSV.CsvTable wrapping the parsed file (first row = header). Read it with the CsvTable methods listed in this group (getLines, getValue, columnNameToInd, createIndexes, getLineCount...). It is undefined until the download finishes: the layer waits for the resource and recalculates on its waitend event, so beforeCalc/expression can rely on it being loaded.

{{loadcsv|id=emissions_csv|url=/theme/app/data/emissoesco2.csv|removeEmptyLines=true}}
beforeCalc: function(inputs) {
    var csv = inputs.id['emissions_csv'];          // columns: percentage, carbon_loss
    var rows = csv.getLines(0, '25');               // rows whose first column equals 25
}

slider · number or range (input)

Slider17 entries

Written in a layer's descriptionHtml as {{slider|parameter=value|...}}

Tool that allows users to create a slider that the user can drag and change its value as map input.
View the complete Slider API here.

backgroundColors

Array.<String># Array of colors of background slider values, to define background slider color based in slider value.

Array of colors of background slider values, to define background slider color based in slider value. The values are defined from most to minimum with two properties each:

  • color: {String} CSS color definition for the current interval. i.e. 'red','black','#FF0000', '#000000'.
  • startValue: {Numeric} If defined define the initial value which above will apply this color, otherwise use theminimum slider value as default.

Ex.:
 backgroundColors: [
     {
         // Define background color to red when slider has value above 75.
         color: "red",
         startValue: 75
     },
     {
         // Define background color starting from value 0.
         // That results two intervals, from 0 to 75 as blue, and from 76 to 100 as red.
         color: "blue",
         startValue: 0
     }
 ]

cls

String# Extra CSS class(es) added to the slider element (appended to the default clickable).

Extra CSS class(es) added to the slider element (appended to the default clickable).

|cls=my_slider|

disabled

Boolean= false# Set true to render the slider disabled (Ext disabled); enable it later with Ext.getCmp(id).enable().

Set true to render the slider disabled (Ext disabled); enable it later with Ext.getCmp(id).enable().

|disabled=true|

fieldLabel

String# Defines the label shown at the left of the slider (Ext fieldLabel).

Defines the label shown at the left of the slider (Ext fieldLabel).

|fieldLabel=Deforestation (%)|

getValue

function() : Number# Returns the current value of a single-thumb slider.

Returns the current value of a single-thumb slider. Call it on the component (Ext.getCmp(id) or a getid= reference), not on inputs.id[ID], which already holds the plain value.

Returns The current slider value.

var v = Ext.getCmp('deforestation_slider').getValue();

getValues

function() : Array.<Number># Returns the value of every thumb; use it for range sliders created with values=[lo, hi].

Returns the value of every thumb; use it for range sliders created with values=[lo, hi].

Returns One value per thumb, in thumb order.

var range = Ext.getCmp('interval_slider').getValues(); // [lo, hi]

gradient

Boolean= false# Set true to blend the backgroundColors into a continuous gradient along the filled part of the slider (each colour fading into the next from its startValue).

Set true to blend the backgroundColors into a continuous gradient along the filled part of the slider (each colour fading into the next from its startValue). With the default false each interval is a solid colour, with a short blend only around the thumb. Has no effect without backgroundColors.

|backgroundColors=[{color: "green", startValue: 0}, {color: "red", startValue: 50}]|gradient=true|

hideLabel

Boolean= false# Set true to hide the label and the space reserved for it (Ext hideLabel).

Set true to hide the label and the space reserved for it (Ext hideLabel).

|hideLabel=true|

id

String# Defines the id to identify the object.

Defines the id to identify the object.

|id=example_slider|

increment

Number# Defines the step of each increment or decrement in the actual value of the slider when being dragged.

Defines the step of each increment or decrement in the actual value of the slider when being dragged.

|increment = 10|

maxValue

Number# Defines the maximum value of the slider.

Defines the maximum value of the slider.

|maxValue = 100|

minValue

Number# Defines the minimum value of the slider.

Defines the minimum value of the slider.

|minValue = 0|

setValue

function(value, animate)# Sets the slider value from code.

Sets the slider value from code. Firing the change event (the default) also updates inputs.id[ID] and recalculates the layer. For a range slider pass the thumb index first: setValue(index, value).

value Number
New value (clamped to minValue/maxValue).
animate Boolean
Set false to move the thumb without animation.
Ext.getCmp('deforestation_slider').setValue(20);

thumbStyle

String= null# Defines extra CSS class(es) added to the slider thumb (the draggable handle), to restyle it.

Defines extra CSS class(es) added to the slider thumb (the draggable handle), to restyle it.

|thumbStyle=x-slider-thumb-cut|

value

Number# Defines the initial value of the slider (default 100).

Defines the initial value of the slider (default 100).

At runtime the same value is what inputs.id[ID] (and inputs[i]) holds in beforeCalc/expression: a number, or an array [lower, upper] when the values range form is used. The layer recalculates on the slider change event (thumb released or value set from code).

|value = 100|
beforeCalc: function(inputs) {
    var threshold = inputs.id['deforestation_slider']; // number
}

values

Array.<Number># Defines the slider interval limits.

Defines the slider interval limits. If defined, the slider will be displayed as a range slider. It's return at the 'inputs' parameter will be a array of two values.

|values = [0, 250]|
|beforeCalc: function(inputs) {
   let lowerValue = inputs[0][0]; // The value of the left drag
   let upperValue = inputs[0][1]; // The value of the right drag
}|

width

Number# Defines the slider width in pixels.

Defines the slider width in pixels. Any other Ext.slider.SingleSlider config (cls, fieldLabel, hideLabel, disabled, style, keyIncrement...) is also passed through unchanged.

|width=200|

label · text

Label6 entries

Written in a layer's descriptionHtml as {{label|parameter=value|...}}

Create a simple label element to display some text.
Usage: {{label|}}
This label is created from Ext.form.Label.
Only some properties are listed here.

cls

String# Extra CSS class(es) added to the <label> element.

Extra CSS class(es) added to the <label> element.

{{label|cls=slider_label|text=0%}}

forId

String# Defines the id of the form field the label is for (rendered as the for attribute of the <label>), so clicking the label focuses that field.

Defines the id of the form field the label is for (rendered as the for attribute of the <label>), so clicking the label focuses that field.

{{label|text=Price (US$)|forId=soy_value}}{{textfield|id=soy_value|isnumeric}}

html

String# Defines the content of the label as raw HTML (not escaped).

Defines the content of the label as raw HTML (not escaped). Everything after html= up to the next | is the value, so it may contain = characters. text wins when both are given.

{{label|cls=slider_label|html=0% <b>...</b> 100%|id=scale_label}}

id

String# Defines the id of the label component (Ext.getCmp(id)), needed to change its text from code or to reference it with getid= in another tag.

Defines the id of the label component (Ext.getCmp(id)), needed to change its text from code or to reference it with getid= in another tag. Generated when omitted.

{{label|id=deforestation_label|text=20%}}

style

String# Inline CSS applied to the <label> element.

Inline CSS applied to the <label> element. Any other Ext.form.Label/Ext.Component config (hidden, width...) is passed through unchanged.

{{label|text=Total|style=font-weight: bold; color: #336699;}}

text

String# Defines the text of the label.

Defines the text of the label. It is HTML-escaped, so use html when markup is needed. Update it later with Ext.getCmp(id).setText(text) (typically from an on_change= listener of a slider or textfield through getid=).

{{label|id=deforestation_label|text=Deforestation: 20%}}

Highcharts · charts

Highcharts1 entry
Highcharts is a library used to easly create interactive charts.
Usage: (Highcharts.chart(DOM_ID, {});)
Highcharts JS has a complete set of examples and a nice documentation that can be accessed here.

chart

function(renderTo, options) : Object# Highcharts is loaded by the calculator and editor pages, so a query can call Highcharts.chart(...) without loading anything: put a container in the layer's descriptionHtml and create the chart …

Written as Highcharts.chart

Highcharts is loaded by the calculator and editor pages, so a query can call Highcharts.chart(...) without loading anything: put a container in the layer's descriptionHtml and create the chart once the panel exists - in onInputsReady, in beforeCalc or in a widget handler - then update it as results arrive instead of recreating it. ExtjsUtils.HIGHCHART.getById(id) gives the chart back from the container's id, which is what makes the update possible from another callback.

The bundled build covers the standard chart types plus highcharts-more. Extra modules are loaded on demand with AsyncLoader.loadScriptOnce (see the Sankey entry).

renderTo String
Id of the container element declared in descriptionHtml.
options Object
The Highcharts configuration object.

Returns The chart instance.

descriptionHtml: '<div id="emissions_chart" style="height:220px"></div>',
functions: {
    drawChart: function (values) {
        var chart = ExtjsUtils.HIGHCHART.getById('emissions_chart');
        if (chart) { chart.series[0].setData(values); return; }
        Highcharts.chart('emissions_chart', {
            chart: { type: 'column' },
            title: { text: 'Emissions by year' },
            xAxis: { categories: ['2020', '2021', '2022'] },
            series: [{ name: 'Mt', data: values }]
        });
    }
}

Free HTML

HTML1 entry
Any HTML is possible to be added to the layer description.

content

String# Free HTML inside a layer's descriptionHtml: everything that is not a {{tag}} is passed through to the panel untouched, so a description can carry headings, images, links, tables and the container …

Free HTML inside a layer's descriptionHtml: everything that is not a {{tag}} is passed through to the panel untouched, so a description can carry headings, images, links, tables and the container elements a chart or a custom control needs. Widgets and HTML mix freely in the same string, and the HTML around a widget is rendered before the widget is created, so an element declared here can already be referenced by an id.

Two things to keep in mind. Inside a {{tag|...}} the pipe separates parameters, so HTML that must live in a parameter goes in html= (verbatim to the end of the value) or has its = escaped as \\=. And the description is written inside a JavaScript string in the query, so quotes have to be escaped or alternated as usual.

descriptionHtml:
  '<h3>Deforestation</h3>' +
  '<p>Pick the minimum area to highlight.</p>' +
  '{{slider|id=threshold|minValue=0|maxValue=100|value=20}}' +
  '<div id="chart_area" style="height:220px"></div>'