Create charts dynamically in Google Earth Engine
In this post, we'll look at leveraging ui
package widgets to create charts dynamically in Google Earth Engine (GEE).
Inspired by Spatial Thoughts' "Creating Publication Quality Charts with GEE" course, this article presents an alternative method for customizing charts in the Code Editor. This approach simplifies creating high-quality charts and offers greater flexibility and control over customization options.
You can access the course material here.
The post is divided into the following parts:
- Setting up the environment
- Creating the pseudo-
DataTable
- Selecting the configuration options
- Checking the range and type of values for the selected options
- Creating the widgets
- Defining the callback functions
Setting up the environment
First, we will create two panels, which will initially be empty:
chartPanel
: used as a container for the chart.controlPanel
: used to organize the control widgets.
var chartPanel = ui.Panel({ style: { position: 'top-center' } });
var controlPanel = ui.Panel({ style: { position: 'middle-left' } });
Map.add(chartPanel);
Map.add(controlPanel);
Creating the pseudo-DataTable
In the context of GEE, a DataTable
(or pseudo-DataTable
) is a data structure that can be represented by a 2-D array or a literal JS object. In both cases, the DataTable
must be a client-side object.
Learn more about the DataTable Class.
We will use the same DataTable
used in section 4.1 Data Table Charts of the course for simplification.
var dataTable = {
cols: [
{ id: 'language', label: 'Programming Language', type: 'string' },
{ id: 'responses', label: 'Number of Responses', type: 'number' },
{ id: 'percentage', label: 'Percentage', type: 'number', role: 'annotation' },
{ id: 'style', label: 'Style', type: 'string', role: 'style' },
],
rows: [
{ c: [{ v: 'Python' }, { v: 178 }, { v: 77.1 }, { v: 'color: #8dd3c7' }] },
{ c: [{ v: 'R' }, { v: 102 }, { v: 44.2 }, { v: 'color: #ffffb3' }] },
{ c: [{ v: 'Javascript' }, { v: 59 }, { v: 25.5 }, { v: 'color: #bebada' }] },
{ c: [{ v: 'Fortran' }, { v: 37 }, { v: 16 }, { v: 'color: #fb8072' }] },
{ c: [{ v: 'Java' }, { v: 32 }, { v: 13.9 }, { v: 'color: #80b1d3' }] },
{ c: [{ v: 'C++' }, { v: 27 }, { v: 11.7 }, { v: 'color: #fdb462' }] },
{ c: [{ v: 'C' }, { v: 18 }, { v: 7.8 }, { v: 'color: #b3de69' }] },
{ c: [{ v: 'PHP' }, { v: 14 }, { v: 6.1 }, { v: 'color: #fccde5' }] },
{ c: [{ v: 'Julia' }, { v: 6 }, { v: 2.6 }, { v: 'color: #d9d9d9' }] },
{ c: [{ v: 'Scala' }, { v: 3 }, { v: 1.3 }, { v: 'color: #bc80bd' }] },
],
};
Selecting the configuration options
For each type of chart, dozens of configuration options can be adjusted.
In this article, we will work with a BarChart
and adjust the chartArea.left
, chartArea.top
, chartArea.width
and chartArea.height
settings.
Checking the range and type of values for the selected options
In the configuration options table, we can obtain important information, such as the type of data the option requires and in which units we must provide it.
We will use percentages instead of pixels to work with the standard 0 to 100% range.
Creating the widgets
First, we will create a JS literal object that will store the configuration options for our chart.
var options = {
title: 'Use of Programming Languages by Users of Big Earth Data'
};
Then, we will create a ui.Slider
for each option we will adjust.
var chartAreaLeft = ui.Slider(0, 100, 0, 1);
var chartAreaTop = ui.Slider(0, 100, 0, 1);
var chartAreaWidth = ui.Slider(0, 100, 0, 1);
var chartAreaHeight = ui.Slider(0, 100, 0, 1);
Note: if we were working with boolean configuration options, we could use a ui.Checkbox
; if it were an option that receives a string with the name or hexadecimal code of a color, we could use a ui.Textbox
.
Defining the callback functions
For each option, we will create a function that:
- Adds the options to the
options
object if it does not exist. - Updates the configuration option value from the widget value.
- Calls the
drawChart
function, which handles creating and updating the chart in the panel.
var drawChart = function() {
var chart = ui.Chart(dataTable, 'BarChart', options);
chartPanel.widgets().reset([chart]);
};
var updateChartAreaLeft = function(value) {
if (!options.chartArea) options.chartArea = {};
options.chartArea.left = value + '%';
drawChart();
};
var updateChartAreaTop = function(value) {
if (!options.chartArea) options.chartArea = {};
options.chartArea.top = value + '%';
drawChart();
};
var updateChartAreaWidth = function(value) {
if (!options.chartArea) options.chartArea = {};
options.chartArea.width = value + '%';
drawChart();
};
var updateChartAreaHeight = function(value) {
if (!options.chartArea) options.chartArea = {};
options.chartArea.height = value + '%';
drawChart();
};
We can organize all the information — widgets and callback functions — into an array of objects and use forEach
to compose the controlPanel
.
var components = [
{
widget: chartAreaLeft,
callback: updateChartAreaLeft,
name: 'chartArea.left',
},
{
widget: chartAreaTop,
callback: updateChartAreaTop,
name: 'chartArea.top',
},
{
widget: chartAreaWidth,
callback: updateChartAreaWidth,
name: 'chartArea.width',
},
{
widget: chartAreaHeight,
callback: updateChartAreaHeight,
name: 'chartArea.height',
},
];
var populateControlPanel = function (components) {
var widget = components.widget;
var callback = components.callback;
var name = components.name;
var nameLabel = ui.Label(name, {
textAlign: 'right',
stretch: 'horizontal',
});
widget.onSlide(callback);
widget.style().set('width', '150px');
var innerPanel = ui.Panel([nameLabel, widget]);
innerPanel.setLayout(ui.Panel.Layout.flow('horizontal'));
controlPanel.widgets().add(innerPanel);
};
components.forEach(populateControlPanel);
The outcome will look like this::
In this script, you can access a more complete version of this exercise, with more options already defined.