#Custom Visualization Framework
##The Basics As of 3.16, Looker has a custom visualization framework available to on-premise (i.e., self-hosted) customers only. This feature allows customers with engineering resources to load their own visualizations into Looker which can then be used with Looker-generated query results as well as part of other Looker features, including Spaces and Dashboards. The visualizations created by each customer will be available to all users on its instance. To access newly loaded visualization types, select the elipsis icon (...) in the Visualization bar, as below:
##How to Create Your Own Visualization
NOTE: This feature requires engineering resources familiar with JavaScript . Additionally, Looker Support cannot help with the troubleshooting of custom JavaScript visualizations.
###Requirements:
- Visualizations must be written in JavaScript. They can leverage the D3 library, the underscore (_)library and the jQuery library.
- Visualizations must be located in the
looker/plugins/visualizations
directory. - Visualization files must start with the following line in order to be registered in our visualization system:
looker.plugins.visualizations.add({<YOUR VIS CODE GOES HERE>});
- Visualizations require the following properties to be set:
id:
A unique id in your system relying on. Ideally, you could prefix it with your company name like looker-bar-chart; however IDs cannot contain the word 'looker.'label:
The display label shown in the drop down list of additional visualizations, shown above.create:
Must be a function. The function receives the following arguments:element
, which is the element referenced in the visualization; andsettings
, which refer to the visualization's settings.update:
Must be a function. It is expected to do the repetitive rendering work, and receives the following arugments:data
, which is the data returned from a query;element
, same as above;settings
, same as above;response.data
, which contains metadata about a data request, like the field list for the data, etc.
- Visualizations have the following methods to assist with error propagation:
addError
andclearError
.addError
expects to receive an object with the following propertiesgroup
,message
andtitle
.clearError
when called with no arguments clears all the errors. When called with a group name, clears all the errors in that group. - Generally, visualizations should be stateless and should only render onto the element passed in.
##Preliminaries:
- Make sure that custom visualizations are enabled on your license (feel free to reach out to support@looker.com to confirm this).
- Once this is confirmed, refresh the license key in Looker's admin panel.
- Restart your Looker.
- On the machine hosting your instance of Looker run
mv ~/looker/plugins/visualizations/examples/looker-heatmap-example.js ~/looker/plugins/visualizations/heatmap.js
- Remove the examples directory (optional).
- Uncomment the code in
heatmap.js
.
##Example:
Users with this license feature enabled can find an example to follow in plugins/visualizations/heatmap-example.js
In the first section of the code, a unique id and label are registered, and (optional) style defaults are set:
(function() {
angular.module('Visualizations')
.run(function(VisualizationManager) {
VisualizationManager.register({
id: 'heatmap',
label: 'Heatmap',
options: {
colorRange: {
type: 'array',
label: 'Color Ranges',
section: 'Style',
placeholder: '#fff, red, etc...'
}
},
In the next section, error handling is set up to ensure that users are guided to the correct configuration of dimensions, measures, and pivots:
handleErrors: function(data, resp) {
if (!resp || !resp.fields) return null;
if (resp.fields.dimensions.length != 1) {
this.addError({
group: 'dimension-req',
title: 'Incompatible Data',
message: 'One dimension is required'
});
return false;
} else {
[...]
},
We then use the create
function to introduce the visualization element (in our example, a table):
create: function(element, settings) {
var $el = $(element);
var table = d3.select(element)
.append('table')
.attr('class', 'heatmap')
.attr('width', '100%')
.attr('height', '100%');
this.update(data, element, settings, resp);
},
We then use the update
function to bind the data to a DOM. We start out with error handling, data prep, and then go through to actually create the visuals elements:
update: function(data, element, settings, resp) {
// handle errors
if (!this.handleErrors(data, resp)) return;
this.clearErrors('color-error');
var colorSettings = settings.colorRange || ['white', 'purple', 'red'];
if (colorSettings.length <= 1) {
this.addError({
group: 'color-error',
title: 'Invalid Setting',
message: 'Colors must have two or more values. Each value is separated by a comma. For example "red, blue, green".'
});
}
// from data object, extract the first and only dimension, the first and only measure, and a single pivot.
var dimension = resp.fields.dimensions[0];
var measure = resp.fields.measures[0];
var pivot = resp.pivots;
[...]
// build table and tinker with aesthetics
var table = d3.select(element)
.select('table');
var tds = trs.selectAll('td')
.data(function(datum) {
var tdData = [];
tdData.push({type: 'dimension', data: datum[dimension.name]});
datum[dimension.name];
var measureData = datum[measure.name];
pivot.forEach(function(pivot) {
tdData.push({type: 'measure', data: measureData[pivot.key]});
});
return tdData;
});
}
I don't think they have anything to do with Spaces.