├── .gitignore
├── LICENSE
├── README.md
├── gulpfile.js
├── heatmap.gif
├── index.js
├── package.json
└── public
├── color_directive.js
├── colors.html
├── colors.js
├── heatmap.html
├── heatmap.js
├── heatmap.less
├── heatmap_tooltip.html
├── heatmap_tooltip.less
├── heatmap_tooltip_directive.js
├── heatmap_vis_params.html
├── lib
├── heatmap_controller.js
└── heatmap_directive.js
├── tooltip_directive.js
└── vis
├── components
├── axis
│ ├── axis.js
│ ├── rotate.js
│ └── truncate.js
├── colorbrewer
│ ├── LICENSE.md
│ └── colorbrewer.js
├── control
│ └── events.js
├── elements
│ ├── g.js
│ ├── rect.js
│ └── text.js
├── layout
│ ├── generator.js
│ └── layout.js
├── legend
│ └── legend.js
├── utils
│ ├── attrs.js
│ ├── builder.js
│ └── valuator.js
└── visualization
│ ├── generator.js
│ ├── heatmap.js
│ └── heatmap_layout.js
└── index.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 |
19 | # node-waf configuration
20 | .lock-wscript
21 |
22 | # Compiled binary addons (http://nodejs.org/api/addons.html)
23 | build/Release
24 |
25 | # Dependency directory
26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
27 | node_modules
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Shelby Sturgis
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Kibana Heatmap Plugin
2 | A Heatmap Plugin for Kibana 4
3 |
4 | 
5 |
6 | ### Requirements
7 | Kibana 4.3+
8 |
9 | ### Installation steps
10 | 1. Download and unpack [Kibana](https://www.elastic.co/downloads/kibana).
11 | 2. From the Kibana root directory, install the plugin with the following command:
12 |
13 | To install the version of this heatmap that works with Kibana 4.3 - 4.5:
14 | ```
15 | $ bin/kibana plugin -i heatmap -u https://github.com/stormpython/heatmap/archive/1.0.0.zip
16 | ```
17 |
18 | *Higher versions of Kibana are not supported yet*
19 |
20 | ### Issues
21 | Please file issues [here](https://github.com/stormpython/heatmap/issues).
22 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | var path = require('path');
3 | var path = require('path');
4 | var mkdirp = require('mkdirp');
5 | var Rsync = require('rsync');
6 | var Promise = require('bluebird');
7 | var eslint = require('gulp-eslint');
8 | var watch = require('gulp-watch');
9 |
10 | var pkg = require('./package.json');
11 |
12 | var kibanaPluginDir = path.resolve(__dirname, '../kibana/installedPlugins/heatmap');
13 |
14 | var include = ['package.json', 'index.js', 'public', 'node_modules'];
15 | var exclude = Object.keys(pkg.devDependencies).map(function (name) {
16 | return path.join('node_modules', name);
17 | });
18 |
19 | function syncPluginTo(dest, done) {
20 | mkdirp(dest, function (err) {
21 | if (err) return done(err);
22 | Promise.all(include.map(function (name) {
23 | var source = path.resolve(__dirname, name);
24 | return new Promise(function (resolve, reject) {
25 | var rsync = new Rsync();
26 | rsync
27 | .source(source)
28 | .destination(dest)
29 | .flags('uav')
30 | .recursive(true)
31 | .set('delete')
32 | .exclude(exclude)
33 | .output(function (data) {
34 | process.stdout.write(data.toString('utf8'));
35 | });
36 | rsync.execute(function (err) {
37 | if (err) {
38 | console.log(err);
39 | return reject(err);
40 | }
41 | resolve();
42 | });
43 | });
44 | }))
45 | .then(function () {
46 | done();
47 | })
48 | .catch(done);
49 | });
50 | }
51 |
52 | gulp.task('sync', function (done) {
53 | syncPluginTo(kibanaPluginDir, done);
54 | });
55 |
56 | gulp.task('lint', function (done) {
57 | return gulp.src(['server/**/*.js', 'public/**/*.js', 'public/**/*.jsx'])
58 | // eslint() attaches the lint output to the eslint property
59 | // of the file object so it can be used by other modules.
60 | .pipe(eslint())
61 | // eslint.format() outputs the lint results to the console.
62 | // Alternatively use eslint.formatEach() (see Docs).
63 | .pipe(eslint.formatEach())
64 | // To have the process exit with an error code (1) on
65 | // lint error, return the stream and pipe to failOnError last.
66 | .pipe(eslint.failOnError());
67 | });
68 |
69 | const batch = require('gulp-batch');
70 |
71 | gulp.task('dev', ['sync'], function (done) {
72 | watch(['package.json', 'index.js', 'public/**/*', 'server/**/*'], batch(function(events, done) {
73 | gulp.start(['sync', 'lint'], done);
74 | }));
75 | });
76 |
--------------------------------------------------------------------------------
/heatmap.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stormpython/heatmap/8f05bdc23ca03cf1b276e710499d8b7ddfde8acc/heatmap.gif
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | module.exports = function (kibana) {
2 | return new kibana.Plugin({
3 | uiExports: {
4 | visTypes: ['plugins/heatmap/heatmap']
5 | }
6 | });
7 | };
8 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "heatmap",
3 | "version": "0.1.0",
4 | "devDependencies": {
5 | "bluebird": "3.1.1",
6 | "gulp": "3.9.0",
7 | "gulp-batch": "1.0.5",
8 | "gulp-eslint": "1.1.1",
9 | "gulp-watch": "4.3.5",
10 | "mkdirp": "0.5.1",
11 | "path": "0.12.7",
12 | "rsync": "0.4.0"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/public/color_directive.js:
--------------------------------------------------------------------------------
1 | var _ = require('lodash');
2 | var colorbrewer = require('plugins/heatmap/vis/components/colorbrewer/colorbrewer');
3 | var gElement = require('plugins/heatmap/vis/components/elements/g');
4 | var rectElement = require('plugins/heatmap/vis/components/elements/rect');
5 |
6 | var module = require('ui/modules').get('heatmap');
7 |
8 | module.directive('colorMap', function () {
9 | function link (scope, element, attrs) {
10 | scope.colorScale = colorbrewer[scope.name];
11 | scope.colors = scope.colorScale[scope.value];
12 | scope.min = _.first(Object.keys(scope.colorScale));
13 | scope.max = _.last(Object.keys(scope.colorScale));
14 |
15 | function render(colors) {
16 | var size = 15;
17 | var padding = 2;
18 | var g = gElement();
19 | var rect = rectElement()
20 | .x(function (d, i) { return i * size + padding; })
21 | .y(0)
22 | .width(size)
23 | .height(size)
24 | .fill(function (d) { return d; })
25 |
26 | function draw(selection) {
27 | selection.each(function (data, index) {
28 | d3.select(this)
29 | .datum(data)
30 | .call(g)
31 | .select('g')
32 | .call(rect);
33 | });
34 | }
35 |
36 | d3.select(element[0]).select('svg.colors')
37 | .datum(colors)
38 | .call(draw);
39 | }
40 |
41 | scope.$watch('name', function (newVal, oldVal) {
42 | scope.colorScale = colorbrewer[newVal];
43 | scope.colors = scope.colorScale[scope.value];
44 | scope.min = _.first(Object.keys(scope.colorScale));
45 | scope.max = _.last(Object.keys(scope.colorScale));
46 | render(scope.colors);
47 | });
48 |
49 | scope.$watch('value', function (newVal, oldVal) {
50 | scope.value = newVal;
51 | scope.colors = scope.colorScale[newVal];
52 | render(scope.colors);
53 | });
54 | }
55 |
56 | return {
57 | restrict: 'E',
58 | scope: {
59 | name: '=',
60 | value: '='
61 | },
62 | template: require('plugins/heatmap/colors.html'),
63 | link: link
64 | };
65 | });
66 |
--------------------------------------------------------------------------------
/public/colors.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/public/colors.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | { displayName: 'Yellow - Green', name: 'YlGn', min: 3, max: 9, value: 6 },
3 | { displayName: 'Yellow - Green - Blue', name: 'YlGnBu', min: 3, max: 9, value: 6 },
4 | { displayName: 'Green - Blue', name: 'GnBu', min: 3, max: 9, value: 6 },
5 | { displayName: 'Blue - Green', name: 'BuGn', min: 3, max: 9, value: 6 },
6 | { displayName: 'Purple - Blue - Green', name: 'PuBuGn', min: 3, max: 9, value: 6 },
7 | { displayName: 'Purple - Blue' , name: 'PuBu' , min: 3, max: 9, value: 6 },
8 | { displayName: 'Blue - Purple', name: 'BuPu', min: 3, max: 9, value: 6 },
9 | { displayName: 'Red - Purple', name: 'RdPu', min: 3, max: 9, value: 6 },
10 | { displayName: 'Purple - Red', name: 'PuRd', min: 3, max: 9, value: 6 },
11 | { displayName: 'Orange - Red', name: 'OrRd', min: 3, max: 9, value: 6 },
12 | { displayName: 'Yellow - Orange - Red', name: 'YlOrRd', min: 3, max: 9, value: 6 },
13 | { displayName: 'Yellow - Orange - Brown', name: 'YlOrBr', min: 3, max: 9, value: 6 },
14 | { displayName: 'Purples', name: 'Purples', min: 3, max: 9, value: 6 },
15 | { displayName: 'Blues', name: 'Blues', min: 3, max: 9, value: 6 },
16 | { displayName: 'Greens', name: 'Greens', min: 3, max: 9, value: 6 },
17 | { displayName: 'Oranges', name: 'Oranges', min: 3, max: 9, value: 6 },
18 | { displayName: 'Reds', name: 'Reds', min: 3, max: 9, value: 6 },
19 | { displayName: 'Greys', name: 'Greys', min: 3, max: 9, value: 6 },
20 | { displayName: 'Purple - Orange', name: 'PuOr', min: 3, max: 11, value: 6 },
21 | { displayName: 'Brown - Blue - Green', name: 'BrBG', min: 3, max: 11, value: 6 },
22 | { displayName: 'Purple - Red - Green', name: 'PRGn', min: 3, max: 11, value: 6 },
23 | { displayName: 'Purple - Yellow - Green', name: 'PiYG', min: 3, max: 11, value: 6 },
24 | { displayName: 'Red - Blue', name: 'RdBu', min: 3, max: 11, value: 6 },
25 | { displayName: 'Red - Grey', name: 'RdGy', min: 3, max: 11, value: 6 },
26 | { displayName: 'Red - Yellow - Blue', name: 'RdYlBu', min: 3, max: 11, value: 6 },
27 | { displayName: 'Spectral', name: 'Spectral', min: 3, max: 11, value: 6 },
28 | { displayName: 'Red - Yellow - Green', name: 'RdYlGn', min: 3, max: 11, value: 6 },
29 | { displayName: 'Accent', name: 'Accent', min: 3, max: 8, value: 6 },
30 | { displayName: 'Dark2', name: 'Dark2', min: 3, max: 8, value: 6 },
31 | { displayName: 'Paired', name: 'Paired', min: 3, max: 12, value: 6 },
32 | { displayName: 'Pastel1', name: 'Pastel1', min: 3, max: 9, value: 6 },
33 | { displayName: 'Pastel2', name: 'Pastel2', min: 3, max: 8, value: 6 },
34 | { displayName: 'Set1', name: 'Set1', min: 3, max: 9, value: 6 },
35 | { displayName: 'Set2', name: 'Set2', min: 3, max: 8, value: 6 },
36 | { displayName: 'Set3', name: 'Set3', min: 3, max: 12, value: 6 }
37 | ];
38 |
--------------------------------------------------------------------------------
/public/heatmap.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/public/heatmap.js:
--------------------------------------------------------------------------------
1 | require('plugins/heatmap/heatmap.less');
2 | require('plugins/heatmap/heatmap_tooltip.less');
3 | require('plugins/heatmap/color_directive.js');
4 | require('plugins/heatmap/lib/heatmap_controller.js');
5 | require('plugins/heatmap/lib/heatmap_directive.js');
6 | require('plugins/heatmap/heatmap_tooltip_directive.js');
7 |
8 |
9 | function HeatmapProvider(Private) {
10 | var TemplateVisType = Private(require('ui/template_vis_type/TemplateVisType'));
11 | var Schemas = Private(require('ui/Vis/Schemas'));
12 | var colors = require('plugins/heatmap/colors.js');
13 |
14 | return new TemplateVisType({
15 | name: 'heatmap',
16 | title: 'Heatmap',
17 | description: 'A heat map is a graphical representation of data where the individual ' +
18 | 'values contained in a matrix are represented as colors.',
19 | icon: 'fa-th',
20 | template: require('plugins/heatmap/heatmap.html'),
21 | params: {
22 | defaults: {
23 | margin: { top: 20, right: 200, bottom: 100, left: 100 },
24 | stroke: '#ffffff',
25 | strokeWidth: 1,
26 | padding: 0,
27 | legendNumberFormat: 'number',
28 | color: colors[0].name,
29 | numberOfColors: 6,
30 | rowAxis: { filterBy: 0 },
31 | columnAxis: { filterBy: 0 }
32 | },
33 | colors: colors,
34 | legendNumberFormats: ['number', 'bytes', 'currency', 'percentage'],
35 | editor: require('plugins/heatmap/heatmap_vis_params.html')
36 | },
37 | schemas: new Schemas([
38 | {
39 | group: 'metrics',
40 | name: 'metric',
41 | title: 'Cell',
42 | min: 1,
43 | aggFilter: ['avg', 'sum', 'count', 'min', 'max', 'median', 'cardinality'],
44 | defaults: [
45 | { schema: 'metric', type: 'count' }
46 | ]
47 | },
48 | {
49 | group: 'buckets',
50 | name: 'columns',
51 | icon: 'fa fa-ellipsis-v',
52 | title: 'Columns',
53 | min: 0,
54 | max: 1,
55 | aggFilter: '!geohash_grid'
56 | },
57 | {
58 | group: 'buckets',
59 | name: 'rows',
60 | icon: 'fa fa-ellipsis-h',
61 | title: 'Rows',
62 | min: 0,
63 | max: 1,
64 | aggFilter: '!geohash_grid'
65 | }
66 | ])
67 | });
68 | }
69 |
70 | require('ui/registry/vis_types').register(HeatmapProvider);
71 |
--------------------------------------------------------------------------------
/public/heatmap.less:
--------------------------------------------------------------------------------
1 | .heatmap-vis {
2 | display: flex;
3 | flex: 1 1 100%;
4 | position: relative;
5 | }
6 |
7 | .axis line,
8 | .axis path {
9 | display: none;
10 | }
11 |
12 | input[type="range"] {
13 | display: inline;
14 | width: 200px;
15 | }
16 |
--------------------------------------------------------------------------------
/public/heatmap_tooltip.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/heatmap_tooltip.less:
--------------------------------------------------------------------------------
1 | @import (reference) "~ui/styles/variables";
2 |
3 | .heatmap-tooltip {
4 | position: absolute;
5 | width: auto;
6 | padding: 5px !important;
7 | background: fadeout(@gray-darker, 7%);
8 | -webkit-border-radius: 4px;
9 | -moz-border-radius: 4px;
10 | border-radius: 4px;
11 | -webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
12 | -moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
13 | box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
14 | pointer-events: none;
15 | white-space: nowrap;
16 | color: #ecf0f1 !important;
17 | }
18 |
19 | .heatmap-tooltip-list {
20 | padding: 0;
21 | list-style-type: none;
22 | }
23 | .heatmap-tooltip-list span{
24 | font-size: 12px;
25 | }
26 | .heatmap-tooltip-list span.key{
27 | font-weight: bold;
28 | }
--------------------------------------------------------------------------------
/public/heatmap_tooltip_directive.js:
--------------------------------------------------------------------------------
1 | var d3 = require("d3");
2 | var _ = require("lodash");
3 | var module = require('ui/modules').get('heatmap');
4 |
5 | module.directive('tooltip', function () {
6 |
7 | function controller($scope) {
8 | $scope.isShown = false;
9 | /*
10 | * Make sure that the items array is populated before tooltip is shown.
11 | * The items variable is an array of objects, e.g.
12 | * [
13 | * { key: "Column", value: "Tuesday" },
14 | * { key: "Row", value: "12pm" },
15 | * { key: "Count", value: 12 }
16 | * ]
17 | */
18 | this.showOnHover = function () {
19 | $scope.isShown = !!($scope.items && _.isArray($scope.items) && $scope.items.length);
20 | };
21 |
22 | this.hideOnOut = function(){
23 | $scope.isShown = false;
24 | };
25 | }
26 |
27 | function link(scope, element, attrs, ctrl) {
28 | function render($scope) {
29 | d3.select(_.first(element))
30 | .style("top", $scope.top + "px")
31 | .style("left", $scope.left + "px");
32 |
33 | ctrl.showOnHover();
34 | }
35 |
36 | scope.$watchGroup(["top", "left", "items"], function (newVal, oldVal, scope) {
37 | render(scope);
38 | }, 250);
39 |
40 | scope.$watch("ngShow", function (newVal) {
41 | ctrl.hideOnOut();
42 | });
43 | };
44 |
45 | return {
46 | restrict: "E",
47 | scope: {
48 | top: "=",
49 | left: "=",
50 | items: "=",
51 | ngShow: "="
52 | },
53 | replace: true,
54 | controller: controller,
55 | link: link,
56 | template: require("plugins/heatmap/heatmap_tooltip.html")
57 | };
58 | });
--------------------------------------------------------------------------------
/public/heatmap_vis_params.html:
--------------------------------------------------------------------------------
1 |
85 |
--------------------------------------------------------------------------------
/public/lib/heatmap_controller.js:
--------------------------------------------------------------------------------
1 | var _ = require('lodash');
2 | var module = require('ui/modules').get('heatmap');
3 |
4 | module.controller('HeatmapController', function ($scope, Private) {
5 | var tabifyAggResponse = Private(require('ui/agg_response/tabify/tabify'));
6 |
7 | function getLabel(agg, name) {
8 | return agg.bySchemaName[name] ? agg.bySchemaName[name][0].makeLabel() : '';
9 | }
10 |
11 | function processTableGroups(tableGroups, $scope) {
12 | var columnAggId = _.first(_.pluck($scope.vis.aggs.bySchemaName['columns'], 'id'));
13 | var rowAggId = _.first(_.pluck($scope.vis.aggs.bySchemaName['rows'], 'id'));
14 | var metricsAggId = _.first(_.pluck($scope.vis.aggs.bySchemaName['metric'], 'id'));
15 | var dataLabels = { [columnAggId]: 'col', [rowAggId]: 'row', [metricsAggId]: 'value' };
16 |
17 | var cells = [];
18 |
19 | tableGroups.tables.forEach(function (table) {
20 | table.rows.forEach(function (row) {
21 | var cell = {};
22 |
23 | table.columns.forEach(function (column, i) {
24 | var fieldFormatter = table.aggConfig(column).fieldFormatter();
25 | // Median metric aggs use the parentId and not the id field
26 | var key = column.aggConfig.parentId ? dataLabels[column.aggConfig.parentId] : dataLabels[column.aggConfig.id];
27 |
28 | if (key) {
29 | cell[key] = key !== 'value' ? fieldFormatter(row[i]) : row[i];
30 | }
31 | });
32 |
33 | // if no columns or rows, then return '_all'
34 | if (!cell.col && !cell.row) {
35 | cell['col'] = '_all';
36 | }
37 |
38 | cells.push(cell);
39 | });
40 | });
41 |
42 | return cells;
43 | };
44 |
45 | $scope.$watch('esResponse', function (resp) {
46 | if (!resp) {
47 | $scope.data = null;
48 | return;
49 | }
50 |
51 | // Add row, column, and metric titles as vis parameters
52 | _.merge($scope.vis.params, {
53 | rowAxis: { title: getLabel($scope.vis.aggs, 'rows') },
54 | columnAxis: { title: getLabel($scope.vis.aggs, 'columns') },
55 | legendTitle: getLabel($scope.vis.aggs, 'metric')
56 | });
57 |
58 | $scope.data = [{
59 | cells: processTableGroups(tabifyAggResponse($scope.vis, resp), $scope)
60 | }];
61 |
62 | $scope.eventListeners = {
63 | mouseover: [ mouseover ],
64 | mouseout: [ mouseout ]
65 | };
66 |
67 | function mouseover(event) {
68 | var target = d3.select(event.target);
69 | var isHeatmapCell = (target.attr("class") === "cell");
70 | var OFFSET = 50;
71 |
72 | if (isHeatmapCell) {
73 | // get data bound to heatmap cell
74 | var d = _.first(target.data());
75 | // Custom code for tooltip functionality goes here
76 | $scope.$apply(function () {
77 | var params = $scope.vis.params;
78 | $scope.tooltipItems = Object.keys(d)
79 | .filter(function (key) { return key !== "data"; })
80 | .map(function (key) {
81 |
82 | var title = d3.selectAll('text.title');
83 | var value = d[key];
84 | if (key.toUpperCase() === 'ROW') {
85 | key = params.columnAxis.title || 'ROW';
86 | }
87 | if (key.toUpperCase() === 'COL') {
88 | key = params.rowAxis.title || 'COL';
89 | }
90 | return {
91 | key: key.toUpperCase(),
92 | value: value
93 | };
94 | });
95 |
96 | $scope.top = d.data.row + parseInt(params.margin.top) + OFFSET;
97 | $scope.left = d.data.col + parseInt(params.margin.left) + OFFSET;
98 | });
99 | }
100 | };
101 |
102 | function mouseout(event){
103 | $scope.$apply(function () {
104 | $scope.tooltipItems = [];
105 | $scope.top = 0;
106 | $scope.left = 0;
107 | });
108 | }
109 | });
110 | });
111 |
--------------------------------------------------------------------------------
/public/lib/heatmap_directive.js:
--------------------------------------------------------------------------------
1 | var d3 = require('d3');
2 | var _ = require('lodash');
3 | var visGenerator = require('plugins/heatmap/vis/index');
4 |
5 | var module = require('ui/modules').get('heatmap');
6 |
7 | module.directive('heatmap', function () {
8 | function link (scope, element, attrs) {
9 | angular.element(document).ready(function () {
10 | var vis = visGenerator();
11 | var svg = d3.select(element[0]);
12 |
13 | function onSizeChange() {
14 | return {
15 | width: element.parent().width(),
16 | height: element.parent().height()
17 | };
18 | }
19 |
20 | function getSize() {
21 | var size = onSizeChange();
22 | return [size.width, size.height];
23 | };
24 |
25 | function render(data, opts, eventListeners) {
26 | var chartSize;
27 |
28 | opts = opts || {};
29 | eventListeners = eventListeners || {};
30 | chartSize = getSize();
31 |
32 | vis.options(opts)
33 | .listeners(eventListeners)
34 | .size(chartSize);
35 |
36 | if (data) {
37 | svg.datum(data).call(vis);
38 | }
39 | };
40 |
41 | scope.$watch('data', function (newVal, oldVal) {
42 | render(newVal, scope.options, scope.eventListeners);
43 | });
44 |
45 | scope.$watch('options', function (newVal, oldVal) {
46 | render(scope.data, newVal, scope.eventListeners);
47 | });
48 |
49 | scope.$watch('eventListeners', function (newVal, oldVal) {
50 | render(scope.data, scope.options, newVal);
51 | });
52 |
53 | scope.$watch(onSizeChange, _.debounce(function () {
54 | render(scope.data, scope.options, scope.eventListeners);
55 | }, 250), true);
56 |
57 | element.bind('resize', function () {
58 | scope.$apply();
59 | });
60 | });
61 | }
62 |
63 | return {
64 | restrict: 'E',
65 | scope: {
66 | data: '=',
67 | options: '=',
68 | eventListeners: '='
69 | },
70 | template: '',
71 | replace: 'true',
72 | link: link
73 | };
74 | });
75 |
--------------------------------------------------------------------------------
/public/tooltip_directive.js:
--------------------------------------------------------------------------------
1 | var _ = require("lodash");
2 | var module = require('ui/modules').get('heatmap');
3 |
4 | module.directive('tooltip', function () {
5 | debugger;
6 | function controller($scope) {
7 | $scope.isShown = false;
8 | debugger;
9 | /*
10 | * Make sure that the items array is populated before tooltip is shown.
11 | * The items variable is an array of objects, e.g.
12 | * [
13 | * { key: "Column", value: "Tuesday" },
14 | * { key: "Row", value: "12pm" },
15 | * { key: "Count", value: 12 }
16 | * ]
17 | */
18 | this.showOnHover = function () {
19 | $scope.isShown = !!($scope.items && _.isArray($scope.items) && $scope.items.length);
20 | };
21 | }
22 |
23 | function link(scope, element, attrs, ctrl) {
24 | function render($scope) {
25 | debugger;
26 | d3.select(_.first(element))
27 | .style("top", $scope.top + "px")
28 | .style("left", $scope.left + "px");
29 |
30 | ctrl.showOnHover();
31 | }
32 |
33 | scope.$watchGroup(["top", "left", "items"], function (newVal, oldVal, scope) {
34 | debugger;
35 | render(scope);
36 | }, 250);
37 | }
38 |
39 | return {
40 | restrict: "E",
41 | scope: {
42 | top: "=",
43 | left: "=",
44 | items: "="
45 | },
46 | replace: true,
47 | controller: controller,
48 | link: link,
49 | template: require("plugins/heatmap/heatmap_tooltip.html")
50 | };
51 | });
--------------------------------------------------------------------------------
/public/vis/components/axis/axis.js:
--------------------------------------------------------------------------------
1 | var d3 = require('d3');
2 | var _ = require('lodash');
3 | var rotate = require('plugins/heatmap/vis/components/axis/rotate');
4 |
5 | function axes() {
6 | var scale = d3.scale.linear();
7 | var orientation = 'left';
8 | var rotateLabels = false;
9 | var rotateOptions = {};
10 | var ticks = {};
11 | var title = {};
12 | var transform = 'translate(0,0)';
13 | var cssClass = 'axis';
14 | var axis = d3.svg.axis();
15 | var rotation = rotate();
16 |
17 | function generator(selection) {
18 | selection.each(function (data) {
19 | axis.orient(orientation)
20 | .scale(scale)
21 | .ticks(ticks.number || 10)
22 | .tickValues(ticks.values || null)
23 | .tickSize(ticks.size || 6)
24 | .innerTickSize(ticks.innerTickSize || 6)
25 | .outerTickSize(ticks.outerTickSize || 6)
26 | .tickPadding(ticks.padding || 3)
27 | .tickFormat(ticks.format || null);
28 |
29 | var g = d3.select(this).selectAll('g.' + cssClass)
30 | .data([data]);
31 |
32 | g.exit().remove();
33 | g.enter().append('g');
34 |
35 | // Attach axis
36 | g.attr('class', cssClass + ' axis')
37 | .attr('transform', transform)
38 | .call(axis);
39 |
40 | if (rotateLabels) {
41 | var axisLength;
42 |
43 | if (_.isFunction(scale.rangeBand)) {
44 | axisLength = Math.abs(_.last(scale.range()) + scale.rangeBand());
45 | } else {
46 | axisLength = Math.abs(scale.range()[1] - scale.range()[0]);
47 | }
48 |
49 | rotation
50 | .axisLength(axisLength)
51 | .measure(rotateOptions.measure || 'width')
52 | .text({
53 | transform: rotateOptions.transform || 'translate(0,0)rotate(-45)'
54 | });
55 |
56 | g.call(rotation);
57 | }
58 |
59 | var text = g.selectAll('text.title')
60 | .data([data]);
61 |
62 | text.exit().remove();
63 | text.enter().append('text')
64 | .attr('class', title.class || 'title');
65 |
66 | text
67 | .attr('x', title.x || 6)
68 | .attr('y', title.y || 6)
69 | .attr('dx', title.dx || '')
70 | .attr('dy', title.dy || '.71em')
71 | .attr('transform', title.transform || 'translate(0,0)')
72 | .style('text-anchor', title.anchor || 'end')
73 | .text(title.text || '');
74 | });
75 | }
76 |
77 | // Public API
78 | generator.scale = function (v) {
79 | if (!arguments.length) return scale;
80 | scale = v;
81 | return generator;
82 | };
83 |
84 | generator.orientation = function (v) {
85 | if (!arguments.length) return orientation;
86 | orientation = v;
87 | return generator;
88 | };
89 |
90 | generator.class = function (v) {
91 | if (!arguments.length) return cssClass;
92 | cssClass = v;
93 | return generator;
94 | };
95 |
96 | generator.transform = function (v) {
97 | if (!arguments.length) return transform;
98 | transform = v;
99 | return generator;
100 | };
101 |
102 | generator.ticks = function (v) {
103 | if (!arguments.length) return ticks;
104 | ticks.number = typeof v.number !== 'undefined' ? v.number : ticks.number;
105 | ticks.values = typeof v.values !== 'undefined' ? v.values : ticks.values;
106 | ticks.size = typeof v.size !== 'undefined' ? v.size : ticks.size;
107 | ticks.padding = typeof v.padding !== 'undefined' ? v.padding : ticks.padding;
108 | ticks.format = typeof v.format !== 'undefined' ? v.format : ticks.format;
109 | ticks.innerTickSize = typeof v.innerTickSize !== 'undefined' ? v.innerTickSize : ticks.innerTickSize;
110 | ticks.outerTickSize = typeof v.outerTickSize !== 'undefined' ? v.outerTickSize : ticks.outerTickSize;
111 | return generator;
112 | };
113 |
114 | generator.rotateLabels = function (v) {
115 | if (!arguments.length) return rotateLabels;
116 | rotateLabels = v;
117 | return generator;
118 | };
119 |
120 | generator.rotateOptions = function (v) {
121 | if (!arguments.length) return rotateOptions;
122 | rotateOptions.measure = typeof v.measure !== 'undefined' ? v.measure : rotateOptions.measure;
123 | rotateOptions.transform = typeof v.transform !== 'undefined' ? v.transform : rotateOptions.transform;
124 | return generator;
125 | };
126 |
127 | generator.title = function (v) {
128 | if (!arguments.length) return title;
129 | title.class = typeof v.class !== 'undefined' ? _.class : title.class;
130 | title.x = typeof v.x !== 'undefined' ? v.x : title.x;
131 | title.y = typeof v.y !== 'undefined' ? v.y : title.y;
132 | title.dx = typeof v.dx !== 'undefined' ? v.dx : title.dx;
133 | title.dy = typeof v.dy !== 'undefined' ? v.dy : title.dy;
134 | title.transform = typeof v.transform !== 'undefined' ? v.transform : title.transform;
135 | title.anchor = typeof v.anchor !== 'undefined' ? v.anchor : title.anchor;
136 | title.text = typeof v.text !== 'undefined' ? v.text : title.text;
137 | return generator;
138 | };
139 |
140 | return generator;
141 | };
142 |
143 | module.exports = axes;
144 |
--------------------------------------------------------------------------------
/public/vis/components/axis/rotate.js:
--------------------------------------------------------------------------------
1 | var d3 = require('d3');
2 | var truncate = require('plugins/heatmap/vis/components/axis/truncate');
3 |
4 | function rotate() {
5 | var axisLength = 100;
6 | var measure = 'width';
7 | var labelPadding = 5;
8 | var truncateLength = 10;
9 | var text = {};
10 |
11 | function component(g) {
12 | g.each(function () {
13 | var ticks = d3.select(this).selectAll('.tick text');
14 | var numOfTicks = ticks[0].length;
15 | var maxTickLabelLength = (axisLength / numOfTicks) - labelPadding;
16 | var isRotated = false;
17 |
18 | ticks.each(function () {
19 | var labelLength = this.getBBox()[measure];
20 | if (labelLength >= maxTickLabelLength) {
21 | isRotated = true;
22 | }
23 | });
24 |
25 | // Rotate and truncate
26 | if (isRotated) {
27 | ticks
28 | .attr('transform', text.transform || 'translate(0,0)rotate(-45)')
29 | .attr('x', text.x || 0)
30 | .attr('y', text.y || 6)
31 | .attr('dx', text.dx || '')
32 | .attr('dy', text.dy || '.71em')
33 | .style('text-anchor', text.anchor || 'end');
34 |
35 | // Truncation logic goes here
36 | ticks.each(function () {
37 | d3.select(this).call(truncate().maxCharLength(truncateLength));
38 | });
39 | } else {
40 | // Default transform
41 | ticks.attr('transform', text.defaultTransform || 'translate(0,0)');
42 | }
43 | });
44 | }
45 |
46 | // Public API
47 | component.axisLength = function (_) {
48 | if (!arguments.length) return axisLength;
49 | axisLength = typeof _ === 'number' ? _ : axisLength;
50 | return component;
51 | };
52 |
53 | component.measure = function (_) {
54 | if (!arguments.length) return measure;
55 | measure = typeof _ === 'string' ? _ : measure;
56 | return component;
57 | };
58 |
59 | component.labelPadding = function (_) {
60 | if (!arguments.length) return labelPadding;
61 | labelPadding = typeof _ === 'number' ? _ : labelPadding;
62 | return component;
63 | };
64 |
65 | component.truncateLength = function (_) {
66 | if (!arguments.length) return truncateLength;
67 | truncateLength = typeof _ === 'number' ? _ : truncateLength;
68 | return component;
69 | };
70 |
71 | component.text = function (_) {
72 | if (!arguments.length) return text;
73 | text.transform = typeof _.transform !== 'undefined' ? _.transform : text.transform;
74 | text.defaultTransform = typeof _.defaultTransform !== 'undefined' ? _.defaultTransform : text.defaultTransform;
75 | text.x = typeof _.x !== 'undefined' ? _.x : text.x;
76 | text.y = typeof _.y !== 'undefined' ? _.y : text.y;
77 | text.dx = typeof _.dx !== 'undefined' ? _.dx : text.dx;
78 | text.dy = typeof _.dy !== 'undefined' ? _.dy : text.dy;
79 | text.anchor = typeof _.anchor !== 'undefined' ? _.anchor : text.anchor;
80 | return component;
81 | };
82 |
83 | return component;
84 | };
85 |
86 | module.exports = rotate;
87 |
--------------------------------------------------------------------------------
/public/vis/components/axis/truncate.js:
--------------------------------------------------------------------------------
1 | var d3 = require('d3');
2 |
3 | function truncate() {
4 | var maxCharLength = 10;
5 |
6 | function component(text) {
7 | text.each(function () {
8 | var txt = d3.select(this);
9 | var labelCharLength = txt.text().length;
10 |
11 | // Shorten label and append ..
12 | if (labelCharLength > maxCharLength) {
13 | var truncatedLabel = txt.text().slice(0, maxCharLength) + '..';
14 | txt.text(truncatedLabel);
15 | }
16 | });
17 | }
18 |
19 | // Public API
20 | component.maxCharLength = function (_) {
21 | if (!arguments.length) return maxCharLength;
22 | maxCharLength = typeof _ === 'number' ? _ : maxCharLength;
23 | return component;
24 | };
25 |
26 | return component;
27 | };
28 |
29 | module.exports = truncate;
30 |
--------------------------------------------------------------------------------
/public/vis/components/colorbrewer/LICENSE.md:
--------------------------------------------------------------------------------
1 | Apache-Style Software License for ColorBrewer software and ColorBrewer Color
2 | Schemes
3 |
4 | Copyright (c) 2002 Cynthia Brewer, Mark Harrower, and The Pennsylvania State
5 | University.
6 |
7 | Licensed under the Apache License, Version 2.0 (the "License"); you may not
8 | use this file except in compliance with the License. You may obtain a copy of
9 | the License at
10 |
11 | http://www.apache.org/licenses/LICENSE-2.0
12 |
13 | Unless required by applicable law or agreed to in writing, software
14 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16 | License for the specific language governing permissions and limitations under
17 | the License.
18 |
19 | Redistribution and use in source and binary forms, with or without
20 | modification, are permitted provided that the following conditions are met:
21 |
22 | 1. Redistributions as source code must retain the above copyright notice, this
23 | list of conditions and the following disclaimer.
24 |
25 | 2. The end-user documentation included with the redistribution, if any, must
26 | include the following acknowledgment: "This product includes color
27 | specifications and designs developed by Cynthia Brewer
28 | (http://colorbrewer.org/)." Alternately, this acknowledgment may appear in the
29 | software itself, if and wherever such third-party acknowledgments normally
30 | appear.
31 |
32 | 4. The name "ColorBrewer" must not be used to endorse or promote products
33 | derived from this software without prior written permission. For written
34 | permission, please contact Cynthia Brewer at cbrewer@psu.edu.
35 |
36 | 5. Products derived from this software may not be called "ColorBrewer", nor
37 | may "ColorBrewer" appear in their name, without prior written permission of
38 | Cynthia Brewer.
39 |
--------------------------------------------------------------------------------
/public/vis/components/colorbrewer/colorbrewer.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | YlGn: {
3 | 3: ["#f7fcb9","#addd8e","#31a354"],
4 | 4: ["#ffffcc","#c2e699","#78c679","#238443"],
5 | 5: ["#ffffcc","#c2e699","#78c679","#31a354","#006837"],
6 | 6: ["#ffffcc","#d9f0a3","#addd8e","#78c679","#31a354","#006837"],
7 | 7: ["#ffffcc","#d9f0a3","#addd8e","#78c679","#41ab5d","#238443","#005a32"],
8 | 8: ["#ffffe5","#f7fcb9","#d9f0a3","#addd8e","#78c679","#41ab5d","#238443","#005a32"],
9 | 9: ["#ffffe5","#f7fcb9","#d9f0a3","#addd8e","#78c679","#41ab5d","#238443","#006837","#004529"]
10 | },
11 | YlGnBu: {
12 | 3: ["#edf8b1","#7fcdbb","#2c7fb8"],
13 | 4: ["#ffffcc","#a1dab4","#41b6c4","#225ea8"],
14 | 5: ["#ffffcc","#a1dab4","#41b6c4","#2c7fb8","#253494"],
15 | 6: ["#ffffcc","#c7e9b4","#7fcdbb","#41b6c4","#2c7fb8","#253494"],
16 | 7: ["#ffffcc","#c7e9b4","#7fcdbb","#41b6c4","#1d91c0","#225ea8","#0c2c84"],
17 | 8: ["#ffffd9","#edf8b1","#c7e9b4","#7fcdbb","#41b6c4","#1d91c0","#225ea8","#0c2c84"],
18 | 9: ["#ffffd9","#edf8b1","#c7e9b4","#7fcdbb","#41b6c4","#1d91c0","#225ea8","#253494","#081d58"]
19 | },
20 | GnBu: {
21 | 3: ["#e0f3db","#a8ddb5","#43a2ca"],
22 | 4: ["#f0f9e8","#bae4bc","#7bccc4","#2b8cbe"],
23 | 5: ["#f0f9e8","#bae4bc","#7bccc4","#43a2ca","#0868ac"],
24 | 6: ["#f0f9e8","#ccebc5","#a8ddb5","#7bccc4","#43a2ca","#0868ac"],
25 | 7: ["#f0f9e8","#ccebc5","#a8ddb5","#7bccc4","#4eb3d3","#2b8cbe","#08589e"],
26 | 8: ["#f7fcf0","#e0f3db","#ccebc5","#a8ddb5","#7bccc4","#4eb3d3","#2b8cbe","#08589e"],
27 | 9: ["#f7fcf0","#e0f3db","#ccebc5","#a8ddb5","#7bccc4","#4eb3d3","#2b8cbe","#0868ac","#084081"]
28 | },
29 | BuGn: {
30 | 3: ["#e5f5f9","#99d8c9","#2ca25f"],
31 | 4: ["#edf8fb","#b2e2e2","#66c2a4","#238b45"],
32 | 5: ["#edf8fb","#b2e2e2","#66c2a4","#2ca25f","#006d2c"],
33 | 6: ["#edf8fb","#ccece6","#99d8c9","#66c2a4","#2ca25f","#006d2c"],
34 | 7: ["#edf8fb","#ccece6","#99d8c9","#66c2a4","#41ae76","#238b45","#005824"],
35 | 8: ["#f7fcfd","#e5f5f9","#ccece6","#99d8c9","#66c2a4","#41ae76","#238b45","#005824"],
36 | 9: ["#f7fcfd","#e5f5f9","#ccece6","#99d8c9","#66c2a4","#41ae76","#238b45","#006d2c","#00441b"]
37 | },
38 | PuBuGn: {
39 | 3: ["#ece2f0","#a6bddb","#1c9099"],
40 | 4: ["#f6eff7","#bdc9e1","#67a9cf","#02818a"],
41 | 5: ["#f6eff7","#bdc9e1","#67a9cf","#1c9099","#016c59"],
42 | 6: ["#f6eff7","#d0d1e6","#a6bddb","#67a9cf","#1c9099","#016c59"],
43 | 7: ["#f6eff7","#d0d1e6","#a6bddb","#67a9cf","#3690c0","#02818a","#016450"],
44 | 8: ["#fff7fb","#ece2f0","#d0d1e6","#a6bddb","#67a9cf","#3690c0","#02818a","#016450"],
45 | 9: ["#fff7fb","#ece2f0","#d0d1e6","#a6bddb","#67a9cf","#3690c0","#02818a","#016c59","#014636"]
46 | },
47 | PuBu: {
48 | 3: ["#ece7f2","#a6bddb","#2b8cbe"],
49 | 4: ["#f1eef6","#bdc9e1","#74a9cf","#0570b0"],
50 | 5: ["#f1eef6","#bdc9e1","#74a9cf","#2b8cbe","#045a8d"],
51 | 6: ["#f1eef6","#d0d1e6","#a6bddb","#74a9cf","#2b8cbe","#045a8d"],
52 | 7: ["#f1eef6","#d0d1e6","#a6bddb","#74a9cf","#3690c0","#0570b0","#034e7b"],
53 | 8: ["#fff7fb","#ece7f2","#d0d1e6","#a6bddb","#74a9cf","#3690c0","#0570b0","#034e7b"],
54 | 9: ["#fff7fb","#ece7f2","#d0d1e6","#a6bddb","#74a9cf","#3690c0","#0570b0","#045a8d","#023858"]
55 | },
56 | BuPu: {
57 | 3: ["#e0ecf4","#9ebcda","#8856a7"],
58 | 4: ["#edf8fb","#b3cde3","#8c96c6","#88419d"],
59 | 5: ["#edf8fb","#b3cde3","#8c96c6","#8856a7","#810f7c"],
60 | 6: ["#edf8fb","#bfd3e6","#9ebcda","#8c96c6","#8856a7","#810f7c"],
61 | 7: ["#edf8fb","#bfd3e6","#9ebcda","#8c96c6","#8c6bb1","#88419d","#6e016b"],
62 | 8: ["#f7fcfd","#e0ecf4","#bfd3e6","#9ebcda","#8c96c6","#8c6bb1","#88419d","#6e016b"],
63 | 9: ["#f7fcfd","#e0ecf4","#bfd3e6","#9ebcda","#8c96c6","#8c6bb1","#88419d","#810f7c","#4d004b"]
64 | },
65 | RdPu: {
66 | 3: ["#fde0dd","#fa9fb5","#c51b8a"],
67 | 4: ["#feebe2","#fbb4b9","#f768a1","#ae017e"],
68 | 5: ["#feebe2","#fbb4b9","#f768a1","#c51b8a","#7a0177"],
69 | 6: ["#feebe2","#fcc5c0","#fa9fb5","#f768a1","#c51b8a","#7a0177"],
70 | 7: ["#feebe2","#fcc5c0","#fa9fb5","#f768a1","#dd3497","#ae017e","#7a0177"],
71 | 8: ["#fff7f3","#fde0dd","#fcc5c0","#fa9fb5","#f768a1","#dd3497","#ae017e","#7a0177"],
72 | 9: ["#fff7f3","#fde0dd","#fcc5c0","#fa9fb5","#f768a1","#dd3497","#ae017e","#7a0177","#49006a"]
73 | },
74 | PuRd: {
75 | 3: ["#e7e1ef","#c994c7","#dd1c77"],
76 | 4: ["#f1eef6","#d7b5d8","#df65b0","#ce1256"],
77 | 5: ["#f1eef6","#d7b5d8","#df65b0","#dd1c77","#980043"],
78 | 6: ["#f1eef6","#d4b9da","#c994c7","#df65b0","#dd1c77","#980043"],
79 | 7: ["#f1eef6","#d4b9da","#c994c7","#df65b0","#e7298a","#ce1256","#91003f"],
80 | 8: ["#f7f4f9","#e7e1ef","#d4b9da","#c994c7","#df65b0","#e7298a","#ce1256","#91003f"],
81 | 9: ["#f7f4f9","#e7e1ef","#d4b9da","#c994c7","#df65b0","#e7298a","#ce1256","#980043","#67001f"]
82 | },
83 | OrRd: {
84 | 3: ["#fee8c8","#fdbb84","#e34a33"],
85 | 4: ["#fef0d9","#fdcc8a","#fc8d59","#d7301f"],
86 | 5: ["#fef0d9","#fdcc8a","#fc8d59","#e34a33","#b30000"],
87 | 6: ["#fef0d9","#fdd49e","#fdbb84","#fc8d59","#e34a33","#b30000"],
88 | 7: ["#fef0d9","#fdd49e","#fdbb84","#fc8d59","#ef6548","#d7301f","#990000"],
89 | 8: ["#fff7ec","#fee8c8","#fdd49e","#fdbb84","#fc8d59","#ef6548","#d7301f","#990000"],
90 | 9: ["#fff7ec","#fee8c8","#fdd49e","#fdbb84","#fc8d59","#ef6548","#d7301f","#b30000","#7f0000"]
91 | },
92 | YlOrRd: {
93 | 3: ["#ffeda0","#feb24c","#f03b20"],
94 | 4: ["#ffffb2","#fecc5c","#fd8d3c","#e31a1c"],
95 | 5: ["#ffffb2","#fecc5c","#fd8d3c","#f03b20","#bd0026"],
96 | 6: ["#ffffb2","#fed976","#feb24c","#fd8d3c","#f03b20","#bd0026"],
97 | 7: ["#ffffb2","#fed976","#feb24c","#fd8d3c","#fc4e2a","#e31a1c","#b10026"],
98 | 8: ["#ffffcc","#ffeda0","#fed976","#feb24c","#fd8d3c","#fc4e2a","#e31a1c","#b10026"],
99 | 9: ["#ffffcc","#ffeda0","#fed976","#feb24c","#fd8d3c","#fc4e2a","#e31a1c","#bd0026","#800026"]
100 | },
101 | YlOrBr: {
102 | 3: ["#fff7bc","#fec44f","#d95f0e"],
103 | 4: ["#ffffd4","#fed98e","#fe9929","#cc4c02"],
104 | 5: ["#ffffd4","#fed98e","#fe9929","#d95f0e","#993404"],
105 | 6: ["#ffffd4","#fee391","#fec44f","#fe9929","#d95f0e","#993404"],
106 | 7: ["#ffffd4","#fee391","#fec44f","#fe9929","#ec7014","#cc4c02","#8c2d04"],
107 | 8: ["#ffffe5","#fff7bc","#fee391","#fec44f","#fe9929","#ec7014","#cc4c02","#8c2d04"],
108 | 9: ["#ffffe5","#fff7bc","#fee391","#fec44f","#fe9929","#ec7014","#cc4c02","#993404","#662506"]
109 | },
110 | Purples: {
111 | 3: ["#efedf5","#bcbddc","#756bb1"],
112 | 4: ["#f2f0f7","#cbc9e2","#9e9ac8","#6a51a3"],
113 | 5: ["#f2f0f7","#cbc9e2","#9e9ac8","#756bb1","#54278f"],
114 | 6: ["#f2f0f7","#dadaeb","#bcbddc","#9e9ac8","#756bb1","#54278f"],
115 | 7: ["#f2f0f7","#dadaeb","#bcbddc","#9e9ac8","#807dba","#6a51a3","#4a1486"],
116 | 8: ["#fcfbfd","#efedf5","#dadaeb","#bcbddc","#9e9ac8","#807dba","#6a51a3","#4a1486"],
117 | 9: ["#fcfbfd","#efedf5","#dadaeb","#bcbddc","#9e9ac8","#807dba","#6a51a3","#54278f","#3f007d"]
118 | },
119 | Blues: {
120 | 3: ["#deebf7","#9ecae1","#3182bd"],
121 | 4: ["#eff3ff","#bdd7e7","#6baed6","#2171b5"],
122 | 5: ["#eff3ff","#bdd7e7","#6baed6","#3182bd","#08519c"],
123 | 6: ["#eff3ff","#c6dbef","#9ecae1","#6baed6","#3182bd","#08519c"],
124 | 7: ["#eff3ff","#c6dbef","#9ecae1","#6baed6","#4292c6","#2171b5","#084594"],
125 | 8: ["#f7fbff","#deebf7","#c6dbef","#9ecae1","#6baed6","#4292c6","#2171b5","#084594"],
126 | 9: ["#f7fbff","#deebf7","#c6dbef","#9ecae1","#6baed6","#4292c6","#2171b5","#08519c","#08306b"]
127 | },
128 | Greens: {
129 | 3: ["#e5f5e0","#a1d99b","#31a354"],
130 | 4: ["#edf8e9","#bae4b3","#74c476","#238b45"],
131 | 5: ["#edf8e9","#bae4b3","#74c476","#31a354","#006d2c"],
132 | 6: ["#edf8e9","#c7e9c0","#a1d99b","#74c476","#31a354","#006d2c"],
133 | 7: ["#edf8e9","#c7e9c0","#a1d99b","#74c476","#41ab5d","#238b45","#005a32"],
134 | 8: ["#f7fcf5","#e5f5e0","#c7e9c0","#a1d99b","#74c476","#41ab5d","#238b45","#005a32"],
135 | 9: ["#f7fcf5","#e5f5e0","#c7e9c0","#a1d99b","#74c476","#41ab5d","#238b45","#006d2c","#00441b"]
136 | },
137 | Oranges: {
138 | 3: ["#fee6ce","#fdae6b","#e6550d"],
139 | 4: ["#feedde","#fdbe85","#fd8d3c","#d94701"],
140 | 5: ["#feedde","#fdbe85","#fd8d3c","#e6550d","#a63603"],
141 | 6: ["#feedde","#fdd0a2","#fdae6b","#fd8d3c","#e6550d","#a63603"],
142 | 7: ["#feedde","#fdd0a2","#fdae6b","#fd8d3c","#f16913","#d94801","#8c2d04"],
143 | 8: ["#fff5eb","#fee6ce","#fdd0a2","#fdae6b","#fd8d3c","#f16913","#d94801","#8c2d04"],
144 | 9: ["#fff5eb","#fee6ce","#fdd0a2","#fdae6b","#fd8d3c","#f16913","#d94801","#a63603","#7f2704"]
145 | },
146 | Reds: {
147 | 3: ["#fee0d2","#fc9272","#de2d26"],
148 | 4: ["#fee5d9","#fcae91","#fb6a4a","#cb181d"],
149 | 5: ["#fee5d9","#fcae91","#fb6a4a","#de2d26","#a50f15"],
150 | 6: ["#fee5d9","#fcbba1","#fc9272","#fb6a4a","#de2d26","#a50f15"],
151 | 7: ["#fee5d9","#fcbba1","#fc9272","#fb6a4a","#ef3b2c","#cb181d","#99000d"],
152 | 8: ["#fff5f0","#fee0d2","#fcbba1","#fc9272","#fb6a4a","#ef3b2c","#cb181d","#99000d"],
153 | 9: ["#fff5f0","#fee0d2","#fcbba1","#fc9272","#fb6a4a","#ef3b2c","#cb181d","#a50f15","#67000d"]
154 | },
155 | Greys: {
156 | 3: ["#f0f0f0","#bdbdbd","#636363"],
157 | 4: ["#f7f7f7","#cccccc","#969696","#525252"],
158 | 5: ["#f7f7f7","#cccccc","#969696","#636363","#252525"],
159 | 6: ["#f7f7f7","#d9d9d9","#bdbdbd","#969696","#636363","#252525"],
160 | 7: ["#f7f7f7","#d9d9d9","#bdbdbd","#969696","#737373","#525252","#252525"],
161 | 8: ["#ffffff","#f0f0f0","#d9d9d9","#bdbdbd","#969696","#737373","#525252","#252525"],
162 | 9: ["#ffffff","#f0f0f0","#d9d9d9","#bdbdbd","#969696","#737373","#525252","#252525","#000000"]
163 | },
164 | PuOr: {
165 | 3: ["#f1a340","#f7f7f7","#998ec3"],
166 | 4: ["#e66101","#fdb863","#b2abd2","#5e3c99"],
167 | 5: ["#e66101","#fdb863","#f7f7f7","#b2abd2","#5e3c99"],
168 | 6: ["#b35806","#f1a340","#fee0b6","#d8daeb","#998ec3","#542788"],
169 | 7: ["#b35806","#f1a340","#fee0b6","#f7f7f7","#d8daeb","#998ec3","#542788"],
170 | 8: ["#b35806","#e08214","#fdb863","#fee0b6","#d8daeb","#b2abd2","#8073ac","#542788"],
171 | 9: ["#b35806","#e08214","#fdb863","#fee0b6","#f7f7f7","#d8daeb","#b2abd2","#8073ac","#542788"],
172 | 10: ["#7f3b08","#b35806","#e08214","#fdb863","#fee0b6","#d8daeb","#b2abd2","#8073ac","#542788","#2d004b"],
173 | 11: ["#7f3b08","#b35806","#e08214","#fdb863","#fee0b6","#f7f7f7","#d8daeb","#b2abd2","#8073ac","#542788","#2d004b"]
174 | },
175 | BrBG: {
176 | 3: ["#d8b365","#f5f5f5","#5ab4ac"],
177 | 4: ["#a6611a","#dfc27d","#80cdc1","#018571"],
178 | 5: ["#a6611a","#dfc27d","#f5f5f5","#80cdc1","#018571"],
179 | 6: ["#8c510a","#d8b365","#f6e8c3","#c7eae5","#5ab4ac","#01665e"],
180 | 7: ["#8c510a","#d8b365","#f6e8c3","#f5f5f5","#c7eae5","#5ab4ac","#01665e"],
181 | 8: ["#8c510a","#bf812d","#dfc27d","#f6e8c3","#c7eae5","#80cdc1","#35978f","#01665e"],
182 | 9: ["#8c510a","#bf812d","#dfc27d","#f6e8c3","#f5f5f5","#c7eae5","#80cdc1","#35978f","#01665e"],
183 | 10: ["#543005","#8c510a","#bf812d","#dfc27d","#f6e8c3","#c7eae5","#80cdc1","#35978f","#01665e","#003c30"],
184 | 11: ["#543005","#8c510a","#bf812d","#dfc27d","#f6e8c3","#f5f5f5","#c7eae5","#80cdc1","#35978f","#01665e","#003c30"]
185 | },
186 | PRGn: {
187 | 3: ["#af8dc3","#f7f7f7","#7fbf7b"],
188 | 4: ["#7b3294","#c2a5cf","#a6dba0","#008837"],
189 | 5: ["#7b3294","#c2a5cf","#f7f7f7","#a6dba0","#008837"],
190 | 6: ["#762a83","#af8dc3","#e7d4e8","#d9f0d3","#7fbf7b","#1b7837"],
191 | 7: ["#762a83","#af8dc3","#e7d4e8","#f7f7f7","#d9f0d3","#7fbf7b","#1b7837"],
192 | 8: ["#762a83","#9970ab","#c2a5cf","#e7d4e8","#d9f0d3","#a6dba0","#5aae61","#1b7837"],
193 | 9: ["#762a83","#9970ab","#c2a5cf","#e7d4e8","#f7f7f7","#d9f0d3","#a6dba0","#5aae61","#1b7837"],
194 | 10: ["#40004b","#762a83","#9970ab","#c2a5cf","#e7d4e8","#d9f0d3","#a6dba0","#5aae61","#1b7837","#00441b"],
195 | 11: ["#40004b","#762a83","#9970ab","#c2a5cf","#e7d4e8","#f7f7f7","#d9f0d3","#a6dba0","#5aae61","#1b7837","#00441b"]
196 | },
197 | PiYG: {
198 | 3: ["#e9a3c9","#f7f7f7","#a1d76a"],
199 | 4: ["#d01c8b","#f1b6da","#b8e186","#4dac26"],
200 | 5: ["#d01c8b","#f1b6da","#f7f7f7","#b8e186","#4dac26"],
201 | 6: ["#c51b7d","#e9a3c9","#fde0ef","#e6f5d0","#a1d76a","#4d9221"],
202 | 7: ["#c51b7d","#e9a3c9","#fde0ef","#f7f7f7","#e6f5d0","#a1d76a","#4d9221"],
203 | 8: ["#c51b7d","#de77ae","#f1b6da","#fde0ef","#e6f5d0","#b8e186","#7fbc41","#4d9221"],
204 | 9: ["#c51b7d","#de77ae","#f1b6da","#fde0ef","#f7f7f7","#e6f5d0","#b8e186","#7fbc41","#4d9221"],
205 | 10: ["#8e0152","#c51b7d","#de77ae","#f1b6da","#fde0ef","#e6f5d0","#b8e186","#7fbc41","#4d9221","#276419"],
206 | 11: ["#8e0152","#c51b7d","#de77ae","#f1b6da","#fde0ef","#f7f7f7","#e6f5d0","#b8e186","#7fbc41","#4d9221","#276419"]
207 | },
208 | RdBu: {
209 | 3: ["#ef8a62","#f7f7f7","#67a9cf"],
210 | 4: ["#ca0020","#f4a582","#92c5de","#0571b0"],
211 | 5: ["#ca0020","#f4a582","#f7f7f7","#92c5de","#0571b0"],
212 | 6: ["#b2182b","#ef8a62","#fddbc7","#d1e5f0","#67a9cf","#2166ac"],
213 | 7: ["#b2182b","#ef8a62","#fddbc7","#f7f7f7","#d1e5f0","#67a9cf","#2166ac"],
214 | 8: ["#b2182b","#d6604d","#f4a582","#fddbc7","#d1e5f0","#92c5de","#4393c3","#2166ac"],
215 | 9: ["#b2182b","#d6604d","#f4a582","#fddbc7","#f7f7f7","#d1e5f0","#92c5de","#4393c3","#2166ac"],
216 | 10: ["#67001f","#b2182b","#d6604d","#f4a582","#fddbc7","#d1e5f0","#92c5de","#4393c3","#2166ac","#053061"],
217 | 11: ["#67001f","#b2182b","#d6604d","#f4a582","#fddbc7","#f7f7f7","#d1e5f0","#92c5de","#4393c3","#2166ac","#053061"]
218 | },
219 | RdGy: {
220 | 3: ["#ef8a62","#ffffff","#999999"],
221 | 4: ["#ca0020","#f4a582","#bababa","#404040"],
222 | 5: ["#ca0020","#f4a582","#ffffff","#bababa","#404040"],
223 | 6: ["#b2182b","#ef8a62","#fddbc7","#e0e0e0","#999999","#4d4d4d"],
224 | 7: ["#b2182b","#ef8a62","#fddbc7","#ffffff","#e0e0e0","#999999","#4d4d4d"],
225 | 8: ["#b2182b","#d6604d","#f4a582","#fddbc7","#e0e0e0","#bababa","#878787","#4d4d4d"],
226 | 9: ["#b2182b","#d6604d","#f4a582","#fddbc7","#ffffff","#e0e0e0","#bababa","#878787","#4d4d4d"],
227 | 10: ["#67001f","#b2182b","#d6604d","#f4a582","#fddbc7","#e0e0e0","#bababa","#878787","#4d4d4d","#1a1a1a"],
228 | 11: ["#67001f","#b2182b","#d6604d","#f4a582","#fddbc7","#ffffff","#e0e0e0","#bababa","#878787","#4d4d4d","#1a1a1a"]
229 | },
230 | RdYlBu: {
231 | 3: ["#fc8d59","#ffffbf","#91bfdb"],
232 | 4: ["#d7191c","#fdae61","#abd9e9","#2c7bb6"],
233 | 5: ["#d7191c","#fdae61","#ffffbf","#abd9e9","#2c7bb6"],
234 | 6: ["#d73027","#fc8d59","#fee090","#e0f3f8","#91bfdb","#4575b4"],
235 | 7: ["#d73027","#fc8d59","#fee090","#ffffbf","#e0f3f8","#91bfdb","#4575b4"],
236 | 8: ["#d73027","#f46d43","#fdae61","#fee090","#e0f3f8","#abd9e9","#74add1","#4575b4"],
237 | 9: ["#d73027","#f46d43","#fdae61","#fee090","#ffffbf","#e0f3f8","#abd9e9","#74add1","#4575b4"],
238 | 10: ["#a50026","#d73027","#f46d43","#fdae61","#fee090","#e0f3f8","#abd9e9","#74add1","#4575b4","#313695"],
239 | 11: ["#a50026","#d73027","#f46d43","#fdae61","#fee090","#ffffbf","#e0f3f8","#abd9e9","#74add1","#4575b4","#313695"]
240 | },
241 | Spectral: {
242 | 3: ["#fc8d59","#ffffbf","#99d594"],
243 | 4: ["#d7191c","#fdae61","#abdda4","#2b83ba"],
244 | 5: ["#d7191c","#fdae61","#ffffbf","#abdda4","#2b83ba"],
245 | 6: ["#d53e4f","#fc8d59","#fee08b","#e6f598","#99d594","#3288bd"],
246 | 7: ["#d53e4f","#fc8d59","#fee08b","#ffffbf","#e6f598","#99d594","#3288bd"],
247 | 8: ["#d53e4f","#f46d43","#fdae61","#fee08b","#e6f598","#abdda4","#66c2a5","#3288bd"],
248 | 9: ["#d53e4f","#f46d43","#fdae61","#fee08b","#ffffbf","#e6f598","#abdda4","#66c2a5","#3288bd"],
249 | 10: ["#9e0142","#d53e4f","#f46d43","#fdae61","#fee08b","#e6f598","#abdda4","#66c2a5","#3288bd","#5e4fa2"],
250 | 11: ["#9e0142","#d53e4f","#f46d43","#fdae61","#fee08b","#ffffbf","#e6f598","#abdda4","#66c2a5","#3288bd","#5e4fa2"]
251 | },
252 | RdYlGn: {
253 | 3: ["#fc8d59","#ffffbf","#91cf60"],
254 | 4: ["#d7191c","#fdae61","#a6d96a","#1a9641"],
255 | 5: ["#d7191c","#fdae61","#ffffbf","#a6d96a","#1a9641"],
256 | 6: ["#d73027","#fc8d59","#fee08b","#d9ef8b","#91cf60","#1a9850"],
257 | 7: ["#d73027","#fc8d59","#fee08b","#ffffbf","#d9ef8b","#91cf60","#1a9850"],
258 | 8: ["#d73027","#f46d43","#fdae61","#fee08b","#d9ef8b","#a6d96a","#66bd63","#1a9850"],
259 | 9: ["#d73027","#f46d43","#fdae61","#fee08b","#ffffbf","#d9ef8b","#a6d96a","#66bd63","#1a9850"],
260 | 10: ["#a50026","#d73027","#f46d43","#fdae61","#fee08b","#d9ef8b","#a6d96a","#66bd63","#1a9850","#006837"],
261 | 11: ["#a50026","#d73027","#f46d43","#fdae61","#fee08b","#ffffbf","#d9ef8b","#a6d96a","#66bd63","#1a9850","#006837"]
262 | },
263 | Accent: {
264 | 3: ["#7fc97f","#beaed4","#fdc086"],
265 | 4: ["#7fc97f","#beaed4","#fdc086","#ffff99"],
266 | 5: ["#7fc97f","#beaed4","#fdc086","#ffff99","#386cb0"],
267 | 6: ["#7fc97f","#beaed4","#fdc086","#ffff99","#386cb0","#f0027f"],
268 | 7: ["#7fc97f","#beaed4","#fdc086","#ffff99","#386cb0","#f0027f","#bf5b17"],
269 | 8: ["#7fc97f","#beaed4","#fdc086","#ffff99","#386cb0","#f0027f","#bf5b17","#666666"]
270 | },
271 | Dark2: {
272 | 3: ["#1b9e77","#d95f02","#7570b3"],
273 | 4: ["#1b9e77","#d95f02","#7570b3","#e7298a"],
274 | 5: ["#1b9e77","#d95f02","#7570b3","#e7298a","#66a61e"],
275 | 6: ["#1b9e77","#d95f02","#7570b3","#e7298a","#66a61e","#e6ab02"],
276 | 7: ["#1b9e77","#d95f02","#7570b3","#e7298a","#66a61e","#e6ab02","#a6761d"],
277 | 8: ["#1b9e77","#d95f02","#7570b3","#e7298a","#66a61e","#e6ab02","#a6761d","#666666"]
278 | },
279 | Paired: {
280 | 3: ["#a6cee3","#1f78b4","#b2df8a"],
281 | 4: ["#a6cee3","#1f78b4","#b2df8a","#33a02c"],
282 | 5: ["#a6cee3","#1f78b4","#b2df8a","#33a02c","#fb9a99"],
283 | 6: ["#a6cee3","#1f78b4","#b2df8a","#33a02c","#fb9a99","#e31a1c"],
284 | 7: ["#a6cee3","#1f78b4","#b2df8a","#33a02c","#fb9a99","#e31a1c","#fdbf6f"],
285 | 8: ["#a6cee3","#1f78b4","#b2df8a","#33a02c","#fb9a99","#e31a1c","#fdbf6f","#ff7f00"],
286 | 9: ["#a6cee3","#1f78b4","#b2df8a","#33a02c","#fb9a99","#e31a1c","#fdbf6f","#ff7f00","#cab2d6"],
287 | 10: ["#a6cee3","#1f78b4","#b2df8a","#33a02c","#fb9a99","#e31a1c","#fdbf6f","#ff7f00","#cab2d6","#6a3d9a"],
288 | 11: ["#a6cee3","#1f78b4","#b2df8a","#33a02c","#fb9a99","#e31a1c","#fdbf6f","#ff7f00","#cab2d6","#6a3d9a","#ffff99"],
289 | 12: ["#a6cee3","#1f78b4","#b2df8a","#33a02c","#fb9a99","#e31a1c","#fdbf6f","#ff7f00","#cab2d6","#6a3d9a","#ffff99","#b15928"]
290 | },
291 | Pastel1: {
292 | 3: ["#fbb4ae","#b3cde3","#ccebc5"],
293 | 4: ["#fbb4ae","#b3cde3","#ccebc5","#decbe4"],
294 | 5: ["#fbb4ae","#b3cde3","#ccebc5","#decbe4","#fed9a6"],
295 | 6: ["#fbb4ae","#b3cde3","#ccebc5","#decbe4","#fed9a6","#ffffcc"],
296 | 7: ["#fbb4ae","#b3cde3","#ccebc5","#decbe4","#fed9a6","#ffffcc","#e5d8bd"],
297 | 8: ["#fbb4ae","#b3cde3","#ccebc5","#decbe4","#fed9a6","#ffffcc","#e5d8bd","#fddaec"],
298 | 9: ["#fbb4ae","#b3cde3","#ccebc5","#decbe4","#fed9a6","#ffffcc","#e5d8bd","#fddaec","#f2f2f2"]
299 | },
300 | Pastel2: {
301 | 3: ["#b3e2cd","#fdcdac","#cbd5e8"],
302 | 4: ["#b3e2cd","#fdcdac","#cbd5e8","#f4cae4"],
303 | 5: ["#b3e2cd","#fdcdac","#cbd5e8","#f4cae4","#e6f5c9"],
304 | 6: ["#b3e2cd","#fdcdac","#cbd5e8","#f4cae4","#e6f5c9","#fff2ae"],
305 | 7: ["#b3e2cd","#fdcdac","#cbd5e8","#f4cae4","#e6f5c9","#fff2ae","#f1e2cc"],
306 | 8: ["#b3e2cd","#fdcdac","#cbd5e8","#f4cae4","#e6f5c9","#fff2ae","#f1e2cc","#cccccc"]
307 | },
308 | Set1: {
309 | 3: ["#e41a1c","#377eb8","#4daf4a"],
310 | 4: ["#e41a1c","#377eb8","#4daf4a","#984ea3"],
311 | 5: ["#e41a1c","#377eb8","#4daf4a","#984ea3","#ff7f00"],
312 | 6: ["#e41a1c","#377eb8","#4daf4a","#984ea3","#ff7f00","#ffff33"],
313 | 7: ["#e41a1c","#377eb8","#4daf4a","#984ea3","#ff7f00","#ffff33","#a65628"],
314 | 8: ["#e41a1c","#377eb8","#4daf4a","#984ea3","#ff7f00","#ffff33","#a65628","#f781bf"],
315 | 9: ["#e41a1c","#377eb8","#4daf4a","#984ea3","#ff7f00","#ffff33","#a65628","#f781bf","#999999"]
316 | },
317 | Set2: {
318 | 3: ["#66c2a5","#fc8d62","#8da0cb"],
319 | 4: ["#66c2a5","#fc8d62","#8da0cb","#e78ac3"],
320 | 5: ["#66c2a5","#fc8d62","#8da0cb","#e78ac3","#a6d854"],
321 | 6: ["#66c2a5","#fc8d62","#8da0cb","#e78ac3","#a6d854","#ffd92f"],
322 | 7: ["#66c2a5","#fc8d62","#8da0cb","#e78ac3","#a6d854","#ffd92f","#e5c494"],
323 | 8: ["#66c2a5","#fc8d62","#8da0cb","#e78ac3","#a6d854","#ffd92f","#e5c494","#b3b3b3"]
324 | },
325 | Set3: {
326 | 3: ["#8dd3c7","#ffffb3","#bebada"],
327 | 4: ["#8dd3c7","#ffffb3","#bebada","#fb8072"],
328 | 5: ["#8dd3c7","#ffffb3","#bebada","#fb8072","#80b1d3"],
329 | 6: ["#8dd3c7","#ffffb3","#bebada","#fb8072","#80b1d3","#fdb462"],
330 | 7: ["#8dd3c7","#ffffb3","#bebada","#fb8072","#80b1d3","#fdb462","#b3de69"],
331 | 8: ["#8dd3c7","#ffffb3","#bebada","#fb8072","#80b1d3","#fdb462","#b3de69","#fccde5"],
332 | 9: ["#8dd3c7","#ffffb3","#bebada","#fb8072","#80b1d3","#fdb462","#b3de69","#fccde5","#d9d9d9"],
333 | 10: ["#8dd3c7","#ffffb3","#bebada","#fb8072","#80b1d3","#fdb462","#b3de69","#fccde5","#d9d9d9","#bc80bd"],
334 | 11: ["#8dd3c7","#ffffb3","#bebada","#fb8072","#80b1d3","#fdb462","#b3de69","#fccde5","#d9d9d9","#bc80bd","#ccebc5"],
335 | 12: ["#8dd3c7","#ffffb3","#bebada","#fb8072","#80b1d3","#fdb462","#b3de69","#fccde5","#d9d9d9","#bc80bd","#ccebc5","#ffed6f"]
336 | }
337 | };
338 |
--------------------------------------------------------------------------------
/public/vis/components/control/events.js:
--------------------------------------------------------------------------------
1 | var d3 = require('d3');
2 | var _ = require('lodash');
3 |
4 | // Adds event listeners to DOM elements
5 | function events() {
6 | var processor = function (e) { return e; };
7 | var listeners = {};
8 | var element;
9 |
10 | function control(selection) {
11 | selection.each(function () {
12 | if (!element) {
13 | element = d3.select(this);
14 | }
15 |
16 | d3.entries(listeners).forEach(function (d) {
17 | element.on(d.key, function () {
18 | d3.event.stopPropagation(); // => event.stopPropagation()
19 | _.forEach(d.value, function (listener) {
20 | listener.call(this, processor(d3.event));
21 | });
22 | });
23 | });
24 | });
25 | }
26 |
27 | // Public API
28 | control.processor = function (v) {
29 | if (!arguments.length) { return processor; }
30 | processor = _.isFunction(v) ? v : processor;
31 | return control;
32 | };
33 |
34 | control.listeners = function (v) {
35 | if (!arguments.length) { return listeners; }
36 | listeners = _.isPlainObject(v) ? v : listeners;
37 | return control;
38 | };
39 |
40 | return control;
41 | };
42 |
43 | module.exports = events;
44 |
--------------------------------------------------------------------------------
/public/vis/components/elements/g.js:
--------------------------------------------------------------------------------
1 | var d3 = require('d3');
2 | var _ = require('lodash');
3 |
4 | function gGenerator() {
5 | var cssClass = 'group';
6 | var transform = 'translate(0,0)';
7 |
8 | function generator(selection) {
9 | selection.each(function (data, index) {
10 | var g = d3.select(this).selectAll('g.' + cssClass)
11 | .data(data);
12 |
13 | g.exit().remove();
14 |
15 | g.enter().append('g')
16 | .attr('class', cssClass);
17 |
18 | g.attr('transform', transform);
19 | });
20 | }
21 |
22 | // Public API
23 | generator.cssClass = function (v) {
24 | if (!arguments.length) { return cssClass; }
25 | cssClass = _.isString(v) ? v : cssClass;
26 | return generator;
27 | };
28 |
29 | generator.transform = function (v) {
30 | if (!arguments.length) { return transform; }
31 | transform = d3.functor(v);
32 | return generator;
33 | };
34 |
35 | return generator;
36 | }
37 |
38 | module.exports = gGenerator;
39 |
--------------------------------------------------------------------------------
/public/vis/components/elements/rect.js:
--------------------------------------------------------------------------------
1 | var d3 = require('d3');
2 |
3 | function rect() {
4 | var color = d3.scale.category10();
5 | var x = function (d) { return d.x; };
6 | var y = function (d) { return d.y; };
7 | var rx = function (d) { return d.rx || 0; };
8 | var ry = function (d) { return d.ry || 0; };
9 | var width = function (d) { return d.width; };
10 | var height = function (d) { return d.height; };
11 | var cssClass = 'cell';
12 | var fill = colorFill;
13 | var stroke = colorFill;
14 | var strokeWidth = 0;
15 | var fillOpacity = 1;
16 | var strokeOpacity;
17 |
18 | function element(selection) {
19 | selection.each(function (data) {
20 | var cells = d3.select(this).selectAll('rect.' + cssClass)
21 | .data(data);
22 |
23 | cells.exit().remove();
24 |
25 | cells.enter().append('rect')
26 | .attr('class', cssClass);
27 |
28 | cells
29 | .attr('x', x)
30 | .attr('y', y)
31 | .attr('rx', rx)
32 | .attr('ry', ry)
33 | .attr('width', width)
34 | .attr('height', height)
35 | .style('fill', fill)
36 | .style('fill-opacity', fillOpacity)
37 | .style('stroke', stroke)
38 | .style('stroke-width', strokeWidth)
39 | .style('stroke-opacity', strokeOpacity);
40 | });
41 | }
42 |
43 | function colorFill(d, i) {
44 | return color(i);
45 | }
46 |
47 | // Public API
48 | element.x = function (_) {
49 | if (!arguments.length) return x;
50 | x = d3.functor(_);
51 | return element;
52 | };
53 |
54 | element.y = function (_) {
55 | if (!arguments.length) return y;
56 | y = d3.functor(_);
57 | return element;
58 | };
59 |
60 | element.rx = function (_) {
61 | if (!arguments.length) return rx;
62 | rx = d3.functor(_);
63 | return element;
64 | };
65 |
66 | element.ry = function (_) {
67 | if (!arguments.length) return ry;
68 | ry = d3.functor(_);
69 | return element;
70 | };
71 |
72 | element.width = function (_) {
73 | if (!arguments.length) return width;
74 | width = d3.functor(_);
75 | return element;
76 | };
77 |
78 | element.height = function (_) {
79 | if (!arguments.length) return height;
80 | height = d3.functor(_);
81 | return element;
82 | };
83 |
84 | element.class= function (_) {
85 | if (!arguments.length) return cssClass;
86 | cssClass = _;
87 | return element;
88 | };
89 |
90 | element.fill = function (_) {
91 | if (!arguments.length) return fill;
92 | fill = _;
93 | return element;
94 | };
95 |
96 | element.fillOpacity = function (_) {
97 | if (!arguments.length) return fillOpacity;
98 | fillOpacity = _;
99 | return element;
100 | };
101 |
102 | element.stroke = function (_) {
103 | if (!arguments.length) return stroke;
104 | stroke = _;
105 | return element;
106 | };
107 |
108 | element.strokeWidth = function (_) {
109 | if (!arguments.length) return strokeWidth;
110 | strokeWidth = _;
111 | return element;
112 | };
113 |
114 | element.strokeOpacity = function (_) {
115 | if (!arguments.length) return strokeOpacity;
116 | strokeOpacity = _;
117 | return element;
118 | };
119 |
120 | return element;
121 | };
122 |
123 | module.exports = rect;
124 |
--------------------------------------------------------------------------------
/public/vis/components/elements/text.js:
--------------------------------------------------------------------------------
1 | var d3 = require('d3');
2 |
3 | function text() {
4 | var x = function (d) { return d.x || 0; };
5 | var y = function (d) { return d.y || 0; };
6 | var dx = function (d) { return d.dx || ''; };
7 | var dy = function (d) { return d.dy || ''; };
8 | var transform = null;
9 | var cssClass = 'text';
10 | var fill = '#ffffff';
11 | var anchor = 'middle';
12 | var texts = '';
13 |
14 | function element(selection) {
15 | selection.each(function (data) {
16 | var text = d3.select(this).selectAll('text.' + cssClass)
17 | .data(data);
18 |
19 | text.exit().remove();
20 |
21 | text.enter().append('text')
22 | .attr('class', cssClass);
23 |
24 | text
25 | .attr('transform', transform)
26 | .attr('x', x)
27 | .attr('y', y)
28 | .attr('dx', dx)
29 | .attr('dy', dy)
30 | .style('fill', fill)
31 | .style('text-anchor', anchor)
32 | .text(texts);
33 | });
34 | }
35 |
36 | // Public API
37 | element.x = function (_) {
38 | if (!arguments.length) { return x; }
39 | x = d3.functor(_);
40 | return element;
41 | };
42 |
43 | element.y = function (_) {
44 | if (!arguments.length) { return y; }
45 | y = d3.functor(_);
46 | return element;
47 | };
48 |
49 | element.dx = function (_) {
50 | if (!arguments.length) { return dx; }
51 | dx = d3.functor(_);
52 | return element;
53 | };
54 |
55 | element.dy = function (_) {
56 | if (!arguments.length) { return dy; }
57 | dy = d3.functor(_);
58 | return element;
59 | };
60 |
61 | element.transform = function (_) {
62 | if (!arguments.length) { return transform; }
63 | transform = _;
64 | return element;
65 | };
66 |
67 | element.class= function (_) {
68 | if (!arguments.length) { return cssClass; }
69 | cssClass = _;
70 | return element;
71 | };
72 |
73 | element.anchor = function (_) {
74 | if (!arguments.length) { return anchor; }
75 | anchor = _;
76 | return element;
77 | };
78 |
79 | element.fill = function (_) {
80 | if (!arguments.length) { return fill; }
81 | fill = _;
82 | return element;
83 | };
84 |
85 | element.text = function (_) {
86 | if (!arguments.length) { return texts; }
87 | texts = _;
88 | return element;
89 | };
90 |
91 | return element;
92 | };
93 |
94 | module.exports = text;
95 |
--------------------------------------------------------------------------------
/public/vis/components/layout/generator.js:
--------------------------------------------------------------------------------
1 | var d3 = require('d3');
2 | var attrs = require('plugins/heatmap/vis/components/utils/attrs');
3 | var baseLayout = require('plugins/heatmap/vis/components/layout/layout');
4 | var gGenerator = require('plugins/heatmap/vis/components/elements/g');
5 |
6 | function layoutGenerator() {
7 | var layout = baseLayout();
8 | var group = gGenerator();
9 |
10 | function generator(selection) {
11 | selection.each(function (data) {
12 | group.cssClass('chart')
13 | .transform(function (d) {
14 | return 'translate(' + d.dx + ',' + d.dy + ')';
15 | });
16 |
17 | d3.select(this)
18 | .datum(layout(data))
19 | .call(group);
20 | });
21 | }
22 |
23 | // Public API
24 | generator.attr = attrs(generator)(layout);
25 |
26 | return generator;
27 | }
28 |
29 | module.exports = layoutGenerator;
30 |
--------------------------------------------------------------------------------
/public/vis/components/layout/layout.js:
--------------------------------------------------------------------------------
1 | var d3 = require('d3');
2 | var _ = require('lodash');
3 |
4 | function formatType(length, type, cols) {
5 | var output = {};
6 |
7 | switch (type) {
8 | case 'grid':
9 | output.rows = cols ? Math.ceil(length / cols) : Math.round(Math.sqrt(length));
10 | output.columns = cols || Math.ceil(Math.sqrt(length));
11 | break;
12 |
13 | case 'columns':
14 | output.rows = 1;
15 | output.columns = length;
16 | break;
17 |
18 | default:
19 | output.rows = length;
20 | output.columns = 1;
21 | break;
22 | }
23 |
24 | return output;
25 | }
26 |
27 | function baseLayout() {
28 | var type = 'grid'; // available types: 'rows', 'columns', 'grid'
29 | var size = [250, 250]; // [width, height]
30 | var rowScale = d3.scale.linear();
31 | var columnScale = d3.scale.linear();
32 | var numOfCols = 0;
33 |
34 | function layout(data) {
35 | var format = formatType(data.length, type, numOfCols);
36 | var rows = format.rows;
37 | var columns = format.columns;
38 | var cellWidth = size[0] / columns;
39 | var cellHeight = size[1] / rows;
40 | var cell = 0;
41 | var newData = [];
42 |
43 | rowScale.domain([0, rows]).range([0, size[1]]);
44 | columnScale.domain([0, columns]).range([0, size[0]]);
45 |
46 | d3.range(rows).forEach(function (row) {
47 | d3.range(columns).forEach(function (col) {
48 | var datum = data[cell];
49 | var obj = {
50 | dx: columnScale(col),
51 | dy: rowScale(row),
52 | width: cellWidth,
53 | height: cellHeight
54 | };
55 |
56 | function reduce(a, b) {
57 | a[b] = datum[b];
58 | return a;
59 | }
60 |
61 | if (!datum) { return; }
62 |
63 | // Do not mutate the original data, return a new object
64 | newData.push(Object.keys(datum).reduce(reduce, obj));
65 | cell += 1;
66 | });
67 | });
68 |
69 | return newData;
70 | }
71 |
72 | // Public API
73 | layout.type = function (v) {
74 | if (!arguments.length) { return type; }
75 | type = _.isString(v) ? v : type;
76 | return layout;
77 | };
78 |
79 | layout.columns = function (v) {
80 | if (!arguments.length) { return numOfCols; }
81 | numOfCols = _.isNumber(v) ? v : numOfCols;
82 | return layout;
83 | };
84 |
85 | layout.size = function (v) {
86 | if (!arguments.length) { return size; }
87 | size = (_.isArray(v) && _.size(v) === 2 && _.all(v, _.isNumber)) ? v : size;
88 | return layout;
89 | };
90 |
91 | return layout;
92 | }
93 |
94 | module.exports = baseLayout;
95 |
--------------------------------------------------------------------------------
/public/vis/components/legend/legend.js:
--------------------------------------------------------------------------------
1 | var d3 = require('d3');
2 | var numeral = require('numeral');
3 | var gGenerator = require('plugins/heatmap/vis/components/elements/g');
4 | var rectGenerator = require('plugins/heatmap/vis/components/elements/rect');
5 | var textGenerator = require('plugins/heatmap/vis/components/elements/text');
6 |
7 | function legend() {
8 | var cssClass = 'legend';
9 | var transform = 'translate(0,0)';
10 | var rectWidth = 20;
11 | var rectHeight = 20;
12 | var fill = function (d) { return d.color; };
13 | var fillOpacity = 1;
14 | var stroke = '#ffffff';
15 | var strokeWidth = 1;
16 | var strokeOpacity = 1;
17 | var textPadding = 5;
18 | var textAnchor = 'start';
19 | var textFill = '#848e96';
20 | var title = 'Legend';
21 | var numberFormat = '0a';
22 | var scale = d3.scale.quantize();
23 | var g = gGenerator();
24 | var block = gGenerator();
25 | var legendGBlock = gGenerator();
26 | var legendTitle = textGenerator();
27 | var rect = rectGenerator();
28 | var svgText = textGenerator();
29 |
30 | function formatNumber(num, format) {
31 | return numeral(num).format(format);
32 | }
33 |
34 | function generator(selection) {
35 | selection.each(function (datum) {
36 | g.cssClass(cssClass).transform(transform);
37 |
38 | legendGBlock.cssClass('legend-title').transform(transform);
39 | legendTitle.x(0).y(-10).dx('').dy('.32em').fill(textFill)
40 | .anchor('start')
41 | .text(title);
42 |
43 | // Add Legend title
44 | d3.select(this)
45 | .datum([[datum]])
46 | .call(legendGBlock)
47 | .selectAll('g.legend-title')
48 | .call(legendTitle);
49 |
50 | // Adds g elements
51 | d3.select(this)
52 | .datum([datum])
53 | .call(g)
54 | .selectAll('g.' + cssClass)
55 | .each(function (data) {
56 | var upperLimit = data.pop();
57 |
58 | block.cssClass('block')
59 | .transform(function (d, i) {
60 | return 'translate(0,' + (rectHeight * i) + ')';
61 | });
62 |
63 | // Adds rects and text
64 | d3.select(this)
65 | .datum(data)
66 | .call(block)
67 | .selectAll('g.block')
68 | .each(function (d, i) {
69 | rect
70 | .class('legend-cell')
71 | .x(0)
72 | .y(0)
73 | .rx(0)
74 | .ry(0)
75 | .width(rectWidth)
76 | .height(rectHeight)
77 | .fill(function (d, i) { return scale(d); })
78 | .fillOpacity(fillOpacity)
79 | .stroke(stroke)
80 | .strokeWidth(strokeWidth)
81 | .strokeOpacity(strokeOpacity);
82 |
83 | svgText
84 | .class('legend-text')
85 | .x(function () { return rectWidth + textPadding; })
86 | .y(function () { return rectHeight / 2; })
87 | .dx('')
88 | .dy('.32em')
89 | .fill(textFill)
90 | .anchor(textAnchor)
91 | .text(function () {
92 | var formattedNumber = formatNumber(Math.round(d), numberFormat);
93 |
94 | if (i === data.length - 1) {
95 | return formattedNumber + ' - ' + formatNumber(Math.round(upperLimit), numberFormat);
96 | }
97 | return formattedNumber + ' - ' + formatNumber(Math.round(data[i + 1]), numberFormat);
98 | });
99 |
100 | d3.select(this)
101 | .datum([d])
102 | .call(rect)
103 | .call(svgText);
104 | });
105 | });
106 | });
107 | }
108 |
109 | // Public API
110 | generator.class = function (v) {
111 | if (!arguments.length) { return cssClass; }
112 | cssClass = v;
113 | return generator;
114 | };
115 |
116 | generator.transform = function (v) {
117 | if (!arguments.length) { return transform; }
118 | transform = v;
119 | return generator;
120 | };
121 |
122 | generator.rectWidth = function (v) {
123 | if (!arguments.length) { return rectWidth; }
124 | rectWidth = v;
125 | return generator;
126 | };
127 |
128 | generator.rectHeight = function (v) {
129 | if (!arguments.length) { return rectHeight; }
130 | rectHeight = v;
131 | return generator;
132 | };
133 |
134 | generator.fill = function (v) {
135 | if (!arguments.length) { return fill; }
136 | fill = v;
137 | return generator;
138 | };
139 |
140 | generator.fillOpacity = function (v) {
141 | if (!arguments.length) { return fillOpacity; }
142 | fillOpacity = v;
143 | return generator;
144 | };
145 |
146 | generator.stroke = function (v) {
147 | if (!arguments.length) { return stroke; }
148 | stroke = v;
149 | return generator;
150 | };
151 |
152 | generator.strokeWidth = function (v) {
153 | if (!arguments.length) { return strokeWidth; }
154 | strokeWidth = v;
155 | return generator;
156 | };
157 |
158 | generator.strokeOpacity = function (v) {
159 | if (!arguments.length) { return strokeOpacity; }
160 | strokeOpacity = v;
161 | return generator;
162 | };
163 |
164 | generator.text = function (v) {
165 | if (!arguments.length) { return text; }
166 | text = v;
167 | return generator;
168 | };
169 |
170 | generator.textPadding = function (v) {
171 | if (!arguments.length) { return textPadding; }
172 | textPadding = v;
173 | return generator;
174 | };
175 |
176 | generator.textAnchor = function (v) {
177 | if (!arguments.length) { return textAnchor; }
178 | textAnchor = v;
179 | return generator;
180 | };
181 |
182 | generator.textFill = function (v) {
183 | if (!arguments.length) { return textFill; }
184 | textFill = v;
185 | return generator;
186 | };
187 |
188 | generator.title = function (v) {
189 | if (!arguments.length) { return title; }
190 | title = v;
191 | return generator;
192 | };
193 |
194 | generator.numberFormat = function (v) {
195 | var formats = {
196 | number: '0a',
197 | currency: '($0a)',
198 | bytes: '0b',
199 | percentage: '0%'
200 | };
201 |
202 | if (!arguments.length) { return numberFormat; }
203 | numberFormat = formats[v] ? formats[v] : formats.number;
204 | return generator;
205 | };
206 |
207 | generator.scale = function (v) {
208 | if (!arguments.length) { return scale; }
209 | scale = v;
210 | return generator;
211 | };
212 |
213 | return generator;
214 | }
215 |
216 | module.exports = legend;
217 |
--------------------------------------------------------------------------------
/public/vis/components/utils/attrs.js:
--------------------------------------------------------------------------------
1 | var _ = require('lodash');
2 | var builder = require('plugins/heatmap/vis/components/utils/builder');
3 |
4 | function attrs(generator) {
5 | return function () {
6 | var funcs = _.toArray(arguments);
7 |
8 | function filterFunctions(arr, attr) {
9 | return _.filter(arr, function (func) {
10 | return _.isFunction(func[attr]);
11 | });
12 | }
13 |
14 | function getValue(arr, attr) {
15 | if (!arr.length) { return; }
16 |
17 | if (arr.length === 1) { return arr[0][attr](); }
18 |
19 | return _.map(arr, function (func) {
20 | return func[attr]();
21 | });
22 | }
23 |
24 | return function (attr, value) {
25 | if (_.isString(attr)) {
26 | if (!value) {
27 | return getValue(filterFunctions(funcs, attr), attr);
28 | }
29 |
30 | _.forEach(filter(funcs, attr), function (func) {
31 | func[attr](value);
32 | });
33 | }
34 |
35 | if (!value && _.isPlainObject(attr)) {
36 | _.forEach(funcs, function (func) {
37 | builder(attr, func);
38 | });
39 | }
40 |
41 | return generator;
42 | };
43 | };
44 | };
45 |
46 | module.exports = attrs;
47 |
--------------------------------------------------------------------------------
/public/vis/components/utils/builder.js:
--------------------------------------------------------------------------------
1 | var d3 = require('d3');
2 | var _ = require('lodash');
3 |
4 | function builder(obj, func) {
5 | if (!_.isPlainObject(obj)) {
6 | throw new Error('builder expects a javascript Object ({}) as its first argument');
7 | }
8 |
9 | if (!_.isFunction(func)) {
10 | throw new Error('builder expects a function as its second argument');
11 | }
12 |
13 | d3.entries(obj).forEach(function (d) {
14 | if (_.isFunction(func[d.key])) {
15 | func[d.key](d.value);
16 | }
17 | });
18 |
19 | return func;
20 | };
21 |
22 | module.exports = builder;
23 |
--------------------------------------------------------------------------------
/public/vis/components/utils/valuator.js:
--------------------------------------------------------------------------------
1 | var d3 = require('d3');
2 | var _ = require('lodash');
3 |
4 | function valuator(v) {
5 | if (_.isFunction(v)) { return v; }
6 | if (_.isString(v) || _.isNumber(v)) {
7 | return function (d) { return d[v]; };
8 | }
9 | return d3.functor(v);
10 | };
11 |
12 | module.exports = valuator;
13 |
--------------------------------------------------------------------------------
/public/vis/components/visualization/generator.js:
--------------------------------------------------------------------------------
1 | var d3 = require('d3');
2 | var _ = require('lodash');
3 | var builder = require('plugins/heatmap/vis/components/utils/builder');
4 | var heatmap = require('plugins/heatmap/vis/components/visualization/heatmap');
5 |
6 | function chartGenerator() {
7 | var opts = {};
8 |
9 | function generator(selection) {
10 | selection.each(function (data) {
11 | var dataOpts = (data && data.options) || {};
12 | var accessor = opts.accessor || dataOpts.accessor || 'cells';
13 | var chart = heatmap()
14 | .width(data.width)
15 | .height(data.height)
16 | .accessor(accessor);
17 |
18 | _.forEach([opts, dataOpts], function (o) {
19 | builder(o, chart);
20 | });
21 |
22 | d3.select(this).call(chart); // Draw Chart
23 | });
24 | }
25 |
26 | // Public API
27 | generator.options = function (v) {
28 | if (!arguments.length) { return opts; }
29 | opts = _.isPlainObject(v) && !_.isArray(v) ? v : opts;
30 | return generator;
31 | };
32 |
33 | return generator;
34 | }
35 |
36 | module.exports = chartGenerator;
37 |
--------------------------------------------------------------------------------
/public/vis/components/visualization/heatmap.js:
--------------------------------------------------------------------------------
1 | var d3 = require('d3');
2 | var _ = require('lodash');
3 | var axis = require('plugins/heatmap/vis/components/axis/axis');
4 | var colorbrewer = require('plugins/heatmap/vis/components/colorbrewer/colorbrewer');
5 | var gGenerator = require('plugins/heatmap/vis/components/elements/g');
6 | var layout = require('plugins/heatmap/vis/components/visualization/heatmap_layout');
7 | var legendGenerator = require('plugins/heatmap/vis/components/legend/legend');
8 | var rect = require('plugins/heatmap/vis/components/elements/rect');
9 | var valuator = require('plugins/heatmap/vis/components/utils/valuator');
10 |
11 | function heatmap() {
12 | var accessor = function (d) { return d; };
13 | var width = 960;
14 | var height = 500;
15 | var margin = { top: 20, right: 20, bottom: 20, left: 50 };
16 | var rowValue = function (d) { return d.row; };
17 | var colValue = function (d) { return d.col; };
18 | var metric = function (d) { return d.value; };
19 | var cAxis = { title: '', filterBy: 0, rotateLabels: true, rotationAngle: '-45' };
20 | var rAxis = { title: '', filterBy: 0, rotateLabels: true, rotationAngle: '-30'};
21 | var padding = 0;
22 | // var sort = { row: false, col: false };
23 | // var reverse = { row: false, col: false };
24 | var cellClass = 'cell';
25 | var colorScale = d3.scale.quantile();
26 | var colors = "PuBuGn";
27 | var numberOfColors = 6;
28 | var opacityScale = d3.scale.linear();
29 | var opacityRange = [1, 1];
30 | var fill = metric;
31 | var fillOpacity = metric;
32 | var stroke = 'none';
33 | var strokeWidth = 0;
34 | var legendTitle = 'Legend';
35 | var legendNumberFormat = 'number';
36 | var gridLayout = layout();
37 | var columnAxis = axis();
38 | var rowAxis = axis();
39 | var cells = rect();
40 | var legend = legendGenerator();
41 | var g = gGenerator();
42 |
43 | // Creates a unique array of items
44 | function getDomain(data, accessor) {
45 | return data
46 | .map(function (item) {
47 | return accessor.call(this, item);
48 | })
49 | .filter(function (item, index, array) {
50 | return array.indexOf(item) === index;
51 | });
52 | }
53 |
54 | function chart(selection) {
55 | selection.each(function (data, index) {
56 | var metrics = accessor.call(this, data, index);
57 | var adjustedWidth = width - margin.left - margin.right;
58 | var adjustedHeight = height - margin.top - margin.bottom;
59 | var colDomain = getDomain(metrics, colValue);
60 | var rowDomain = getDomain(metrics, rowValue);
61 | var colorRange = colorbrewer[colors][numberOfColors];
62 | var colorDomain = [Math.min(0, d3.min(metrics, metric)), Math.max(d3.max(metrics, metric), 1)];
63 | var columnScale = d3.scale.ordinal()
64 | .domain(colDomain)
65 | .rangeBands([0, adjustedWidth], padding);
66 | var rowScale = d3.scale.ordinal()
67 | .domain(rowDomain)
68 | .rangeBands([0, adjustedHeight], padding);
69 | var container;
70 |
71 | gridLayout
72 | .row(rowValue)
73 | .column(colValue)
74 | .value(metric)
75 | .columnScale(columnScale)
76 | .rowScale(rowScale)
77 | .fill(fill)
78 | .fillScale(colorScale.domain(colorDomain).range(colorRange))
79 | .fillOpacity(fillOpacity)
80 | .opacityScale(opacityScale.domain(colorDomain).range(opacityRange));
81 |
82 | columnAxis
83 | .scale(columnScale)
84 | .class('column')
85 | .orientation('bottom')
86 | .transform('translate(0,' + adjustedHeight + ')')
87 | .ticks({
88 | values: columnScale.domain().filter(function (d, i) {
89 | return !(i % cAxis.filterBy);
90 | })
91 | })
92 | .title({
93 | x: adjustedWidth / 2,
94 | y: margin.bottom * (2 / 3),
95 | anchor: 'middle',
96 | text: cAxis.title
97 | })
98 | .rotateLabels(cAxis.rotateLabels)
99 | .rotateOptions({
100 | transform: 'translate(0,0)rotate(' + cAxis.rotationAngle + ')'
101 | });
102 |
103 | rowAxis
104 | .scale(rowScale)
105 | .class('row')
106 | .ticks({
107 | values: rowScale.domain().filter(function (d, i) {
108 | return !(i % rAxis.filterBy);
109 | })
110 | })
111 | .title({
112 | transform: 'rotate(-90)',
113 | x: -adjustedHeight / 2,
114 | y: -margin.left * (8 / 9),
115 | anchor: 'middle',
116 | text: rAxis.title
117 | })
118 | .rotateLabels(rAxis.rotateLabels)
119 | .rotateOptions({
120 | transform: 'translate(-10,-8)rotate(' + rAxis.rotationAngle + ')',
121 | measure: 'height'
122 | });
123 |
124 | cells
125 | .class(cellClass)
126 | .x(function (d) { return d.data.col; })
127 | .y(function (d) { return d.data.row; })
128 | .width(columnScale.rangeBand())
129 | .height(rowScale.rangeBand())
130 | .fill(function (d) { return d.data.fill; })
131 | .fillOpacity(function (d) { return d.data.opacity; })
132 | .stroke(stroke)
133 | .strokeWidth(strokeWidth);
134 |
135 | legend
136 | .transform(function () {
137 | var x = adjustedWidth + (margin.right / 9);
138 | var y = adjustedHeight - Math.floor(adjustedHeight * (2 / 3));
139 | return 'translate(' + x + ',' + y + ')';
140 | })
141 | .scale(colorScale)
142 | .numberFormat(legendNumberFormat)
143 | .title(legendTitle);
144 |
145 | g.cssClass('container')
146 | .transform('translate(' + margin.left + ',' + margin.top + ')');
147 |
148 | container = d3.select(this)
149 | .datum([{}])
150 | .call(g) // One container to rule them all!
151 | .select('g.container');
152 |
153 | // Draw Heatmap Chart
154 | container
155 | .datum(gridLayout(metrics))
156 | .call(columnAxis)
157 | .call(rowAxis)
158 | .call(cells);
159 |
160 | // Legend
161 | container
162 | .datum([Math.min(0, colorScale.domain()[0])].concat(colorScale.quantiles()).concat(colorScale.domain()[1]))
163 | .call(legend);
164 | });
165 | }
166 |
167 | // Public API
168 | chart.accessor = function (v) {
169 | if (!arguments.length) { return accessor; }
170 | accessor = valuator(v);
171 | return chart;
172 | };
173 |
174 | chart.width = function (v) {
175 | if (!arguments.length) { return width; }
176 | width = v;
177 | return chart;
178 | };
179 |
180 | chart.height = function (v) {
181 | if (!arguments.length) { return height; }
182 | height = v;
183 | return chart;
184 | };
185 |
186 | chart.margin = function (v) {
187 | if (!arguments.length) { return margin; }
188 | margin.top = typeof v.top !== 'undefined' ? v.top : margin.top;
189 | margin.right = typeof v.right !== 'undefined' ? v.right : margin.right;
190 | margin.bottom = typeof v.bottom !== 'undefined' ? v.bottom : margin.bottom;
191 | margin.left = typeof v.left !== 'undefined' ? v.left : margin.left;
192 | return chart;
193 | };
194 |
195 | chart.row = function (v) {
196 | if (!arguments.length) { return rowValue; }
197 | rowValue = valuator(v);
198 | return chart;
199 | };
200 |
201 | chart.column = function (v) {
202 | if (!arguments.length) { return colValue; }
203 | colValue = valuator(v);
204 | return chart;
205 | };
206 |
207 | chart.value = function (v) {
208 | if (!arguments.length) { return metric; }
209 | metric = valuator(v);
210 | return chart;
211 | };
212 |
213 | chart.columnAxis = function (v) {
214 | if (!arguments.length) { return cAxis; }
215 | cAxis.title = typeof v.title !== 'undefined' ? v.title : cAxis.title;
216 | cAxis.filterBy = typeof v.filterBy !== 'undefined' ? v.filterBy : cAxis.filterBy;
217 | return chart;
218 | };
219 |
220 | chart.rowAxis = function (v) {
221 | if (!arguments.length) { return rAxis; }
222 | rAxis.title = typeof v.title !== 'undefined' ? v.title : rAxis.title;
223 | rAxis.filterBy = typeof v.filterBy !== 'undefined' ? v.filterBy : rAxis.filterBy;
224 | return chart;
225 | };
226 |
227 | chart.padding = function (v) {
228 | if (!arguments.length) { return padding; }
229 | padding = v;
230 | return chart;
231 | };
232 |
233 | chart.class = function (v) {
234 | if (!arguments.length) { return cellClass; }
235 | cellClass = v;
236 | return chart;
237 | };
238 |
239 | chart.colorScale = function (v) {
240 | if (!arguments.length) { return colorScale; }
241 | colorScale = v;
242 | return chart;
243 | };
244 |
245 | chart.color = function (v) {
246 | if (!arguments.length) { return colors; }
247 | colors = v;
248 | return chart;
249 | };
250 |
251 | chart.numberOfColors = function (v) {
252 | if (!arguments.length) { return numberOfColors; }
253 | numberOfColors = v;
254 | return chart;
255 | };
256 |
257 | chart.opacityScale = function (v) {
258 | if (!arguments.length) { return opacityScale; }
259 | opacityScale = v;
260 | return chart;
261 | };
262 |
263 | chart.opacityRange = function (v) {
264 | if (!arguments.length) { return opacityRange; }
265 | opacityRange = v;
266 | return chart;
267 | };
268 |
269 | chart.fill = function (v) {
270 | if (!arguments.length) { return fill; }
271 | fill = v;
272 | return chart;
273 | };
274 |
275 | chart.fillOpacity = function (v) {
276 | if (!arguments.length) { return fillOpacity; }
277 | fillOpacity = v;
278 | return chart;
279 | };
280 |
281 | chart.stroke = function (v) {
282 | if (!arguments.length) { return stroke; }
283 | stroke = v;
284 | return chart;
285 | };
286 |
287 | chart.strokeWidth = function (v) {
288 | if (!arguments.length) { return strokeWidth; }
289 | strokeWidth = v;
290 | return chart;
291 | };
292 |
293 | chart.legendTitle = function (v) {
294 | if (!arguments.length) { return legendTitle; }
295 | legendTitle = v;
296 | return chart;
297 | };
298 |
299 | chart.legendNumberFormat = function (v) {
300 | if (!arguments.length) { return legendNumberFormat; }
301 | legendNumberFormat = v;
302 | return chart;
303 | };
304 |
305 | return chart;
306 | }
307 |
308 | module.exports = heatmap;
309 |
--------------------------------------------------------------------------------
/public/vis/components/visualization/heatmap_layout.js:
--------------------------------------------------------------------------------
1 | var d3 = require('d3');
2 |
3 | function heatmapLayout() {
4 | var row = function (d) { return d.row; };
5 | var column = function (d) { return d.col; };
6 | var value = function (d) { return d.value; };
7 | var fill = value;
8 | var fillOpacity = value;
9 | var rowScale = d3.scale.linear();
10 | var columnScale = d3.scale.linear();
11 | var fillScale = d3.scale.linear();
12 | var opacityScale = d3.scale.linear().range([1, 1]);
13 |
14 | function layout(data) {
15 | return data.map(function (d, i) {
16 | return {
17 | row: row.call(data, d, i),
18 | col: column.call(data, d, i),
19 | value: value.call(data, d, i),
20 | data: {
21 | row: rowScale(row.call(data, d, i)),
22 | col: columnScale(column.call(data, d, i)),
23 | fill: fillScale(fill.call(data, d, i)),
24 | opacity: opacityScale(fillOpacity.call(data, d, i))
25 | }
26 | };
27 | });
28 | }
29 |
30 | // Public API
31 | layout.row = function (v) {
32 | if (!arguments.length) { return row; }
33 | row = v;
34 | return layout;
35 | };
36 |
37 | layout.column = function (v) {
38 | if (!arguments.length) { return column; }
39 | column = v;
40 | return layout;
41 | };
42 |
43 | layout.value = function (v) {
44 | if (!arguments.length) { return value; }
45 | value = v;
46 | return layout;
47 | };
48 |
49 | layout.fill = function (v) {
50 | if (!arguments.length) { return fill; }
51 | fill = v;
52 | return layout;
53 | };
54 |
55 | layout.fillOpacity = function (v) {
56 | if (!arguments.length) { return fillOpacity; }
57 | fillOpacity = v;
58 | return layout;
59 | };
60 |
61 | layout.rowScale = function (v) {
62 | if (!arguments.length) { return rowScale; }
63 | rowScale = v;
64 | return layout;
65 | };
66 |
67 | layout.columnScale = function (v) {
68 | if (!arguments.length) { return columnScale; }
69 | columnScale = v;
70 | return layout;
71 | };
72 |
73 | layout.fillScale = function (v) {
74 | if (!arguments.length) { return fillScale; }
75 | fillScale = v;
76 | return layout;
77 | };
78 |
79 | layout.opacityScale = function (v) {
80 | if (!arguments.length) { return opacityScale; }
81 | opacityScale = v;
82 | return layout;
83 | };
84 |
85 | return layout;
86 | }
87 |
88 | module.exports = heatmapLayout;
89 |
--------------------------------------------------------------------------------
/public/vis/index.js:
--------------------------------------------------------------------------------
1 | var d3 = require('d3');
2 | var _ = require('lodash');
3 | var control = require('plugins/heatmap/vis/components/control/events');
4 | var layoutGenerator = require('plugins/heatmap/vis/components/layout/generator');
5 | var chartGenerator = require('plugins/heatmap/vis/components/visualization/generator');
6 |
7 | function vis() {
8 | var events = control();
9 | var layout = layoutGenerator();
10 | var chart = chartGenerator();
11 | var opts = {};
12 | var listeners = {};
13 | var size = [250, 250];
14 |
15 | function generator(selection) {
16 | selection.each(function (data) {
17 | events.listeners(listeners);
18 |
19 | layout.attr({
20 | type: opts.layout || 'grid',
21 | columns: opts.numOfColumns || 0,
22 | size: size
23 | });
24 |
25 | chart.options(opts);
26 |
27 | d3.select(this)
28 | .attr('width', '100%')
29 | .attr('height', size[1])
30 | .call(events)
31 | .call(layout)
32 | .selectAll('g.chart')
33 | .call(chart);
34 | });
35 | }
36 |
37 | // Public API
38 |
39 | generator.options = function (v) {
40 | if (!arguments.length) { return opts; }
41 | opts = _.isPlainObject(v) ? v : opts;
42 | return generator;
43 | };
44 |
45 | generator.listeners = function (v) {
46 | if (!arguments.length) { return listeners; }
47 | listeners = _.isPlainObject(v) ? v : listeners;
48 | return generator;
49 | };
50 |
51 | generator.size = function (v) {
52 | if (!arguments.length) { return size; }
53 | size = (_.isArray(v) && _.size(v) === 2) ? v : size;
54 | return generator;
55 | };
56 |
57 | return generator;
58 | }
59 |
60 | module.exports = vis;
61 |
--------------------------------------------------------------------------------