├── .gitignore ├── Capture1.PNG ├── README.md ├── index.js ├── package.json ├── public ├── kbn_sankey_vis.html ├── kbn_sankey_vis.js ├── kbn_sankey_vis.less ├── kbn_sankey_vis_controller.js └── lib │ └── agg_response.js ├── releases └── 5.5 │ ├── .jshintrc │ ├── README.md │ ├── index.js │ ├── package.json │ ├── public │ ├── kbn_sankey_vis.html │ ├── kbn_sankey_vis.js │ ├── kbn_sankey_vis.less │ ├── kbn_sankey_vis_controller.js │ └── lib │ │ └── agg_response.js │ └── sankey_5_5_Screenshot1.PNG └── sankey_5_5_Screenshot1.PNG /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | -------------------------------------------------------------------------------- /Capture1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuanCarniglia/kbn_sankey_vis/504f3e93af58bb3c0c52df0ed6252dacbca30229/Capture1.PNG -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kibana Sankey Diagram Plugin 2 | 3 | This is a sankey diagram visType plugin for Kibana 5.5+. 4 | 5 | This plugin was developped from . 6 | 7 | Here is an example: 8 | 9 | ![Sankey](sankey_5_5_Screenshot1.PNG) 10 | 11 | # Install 12 | 13 | ``` 14 | git clone https://github.com/JuanCarniglia/kbn_sankey_vis.git 15 | cd kbn_sankey_vis/releases/5.5 16 | npm install d3-sankey-plugin 17 | npm run build 18 | cp -R build/kbn_sankey_vis KIBANA_FOLDER_PATH/installedPlugins/ 19 | ``` 20 | 21 | ** Note that in NTFS file systems, file paths that exceed 260 characters will fail with cp, you have to use ROBOCOPY: 22 | 23 | ``` 24 | robocopy /S build/kbn_sankey_vis KIBANA_FOLDER_PATH/installedPlugins/kbn_sankey_vis 25 | ``` 26 | 27 | ** Also note that if npm run build fails, with a rsync.js error, it is likelly that you don't have RSYNC.EXE installed 28 | in your system, and also that you don't have it on your PATH environment variable. 29 | 30 | Install it from https://www.itefix.net/cwrsync and run: 31 | 32 | ``` 33 | set PATH=%PATH%;{rsync installation directory}\bin 34 | ``` 35 | 36 | # Uninstall 37 | 38 | ``` 39 | bin/kibana plugin --remove kbn_sankey_vis 40 | ``` 41 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | export default function (kibana) { 2 | 3 | return new kibana.Plugin({ 4 | uiExports: { 5 | visTypes: [ 6 | 'plugins/kbn_sankey_vis/kbn_sankey_vis' 7 | ] 8 | } 9 | }); 10 | }; 11 | 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kbn_sankey_vis", 3 | "version": "5.2.1", 4 | "authors": [ 5 | "Chenryn " 6 | ], 7 | "description": "Sankey Diagram Visualization Plugin", 8 | "license": "Apache-2.0", 9 | "homepage": "https://github.com/WolfangAukang/kbn_sankey_vis", 10 | "repository": "https://github.com/WolfangAukang/kbn_sankey_vis.git", 11 | "dependencies": { 12 | "d3-plugins-sankey-fixed": "WolfangAukang/d3-plugins-sankey" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /public/kbn_sankey_vis.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | -------------------------------------------------------------------------------- /public/kbn_sankey_vis.js: -------------------------------------------------------------------------------- 1 | 2 | import 'ui/agg_table'; 3 | import 'ui/agg_table/agg_table_group'; 4 | 5 | import 'plugins/kbn_sankey_vis/kbn_sankey_vis.less'; 6 | import 'plugins/kbn_sankey_vis/kbn_sankey_vis_controller'; 7 | 8 | import TemplateVisTypeTemplateVisTypeProvider from 'ui/template_vis_type/template_vis_type'; 9 | import VisSchemasProvider from 'ui/vis/schemas'; 10 | import kbnSankeyVisTemplate from 'plugins/kbn_sankey_vis/kbn_sankey_vis.html'; 11 | 12 | require('ui/registry/vis_types').register(KbnSankeyVisProvider); 13 | 14 | function KbnSankeyVisProvider(Private) { 15 | let TemplateVisType = Private(TemplateVisTypeTemplateVisTypeProvider); 16 | let Schemas = Private(VisSchemasProvider); 17 | 18 | return new TemplateVisType({ 19 | name: 'kbn_sankey', 20 | title: 'Sankey Diagram', 21 | icon: 'fa-random', 22 | description: 'Sankey charts are ideal for displaying the material, energy and cost flows.', 23 | template: kbnSankeyVisTemplate, 24 | params: { 25 | defaults: { 26 | showMetricsAtAllLevels: false 27 | }, 28 | editor: '' 29 | }, 30 | hierarchicalData: function (vis) { 31 | return Boolean(vis.params.showPartialRows || vis.params.showMetricsAtAllLevels); 32 | }, 33 | schemas: new Schemas([{ 34 | group: 'metrics', 35 | name: 'metric', 36 | title: 'Split Size', 37 | min: 1, 38 | max: 1, 39 | defaults: [{ 40 | type: 'count', 41 | schema: 'metric' 42 | }] 43 | }, { 44 | group: 'buckets', 45 | name: 'segment', 46 | title: 'Split Slices', 47 | aggFilter: '!geohash_grid', 48 | min: 0, 49 | max: Infinity 50 | }]), 51 | requiresSearch: true 52 | }); 53 | } 54 | -------------------------------------------------------------------------------- /public/kbn_sankey_vis.less: -------------------------------------------------------------------------------- 1 | .node rect { 2 | cursor: move; 3 | fill-opacity: .9; 4 | shape-rendering: crispEdges; 5 | } 6 | 7 | .node text { 8 | pointer-events: none; 9 | text-shadow: 0 1px 0 #fff; 10 | } 11 | 12 | .link { 13 | fill: none; 14 | stroke: #000; 15 | stroke-opacity: .2; 16 | } 17 | 18 | .link:hover { 19 | stroke-opacity: .5; 20 | } 21 | -------------------------------------------------------------------------------- /public/kbn_sankey_vis_controller.js: -------------------------------------------------------------------------------- 1 | import uiModules from 'ui/modules'; 2 | 3 | const module = uiModules.get('kibana/kbn_sankey_vis', ['kibana']); 4 | 5 | import d3 from 'd3'; 6 | import _ from 'lodash'; 7 | import $ from 'jquery'; 8 | import S from 'd3-plugins-sankey-fixed'; 9 | 10 | import AggResponseProvider from './lib/agg_response'; 11 | 12 | module.controller('KbnSankeyVisController', function ($scope, $element, $rootScope, Private) { 13 | const sankeyAggResponse = Private(AggResponseProvider); 14 | 15 | let svgRoot = $element[0]; 16 | let color = d3.scale.category10(); 17 | let margin = 20; 18 | let width; 19 | let height; 20 | let div; 21 | let svg; 22 | let globalData = null; 23 | 24 | let _updateDimensions = function _updateDimensions() { 25 | let delta = 10; 26 | let w = $(svgRoot).parent().width() - 10; 27 | let h = $(svgRoot).parent().height() - 40; 28 | if (w) { 29 | if (w > delta) { 30 | w -= delta; 31 | } 32 | width = w; 33 | } 34 | if (h) { 35 | if (h > delta) { 36 | h -= delta; 37 | } 38 | height = h; 39 | } 40 | }; 41 | 42 | 43 | let _buildVis = function (data) { 44 | _updateDimensions(); 45 | 46 | d3.select(svgRoot).selectAll('svg').remove(); 47 | 48 | let energy = data.slices; 49 | div = d3.select(svgRoot); 50 | if (!energy.nodes.length) return; 51 | 52 | svg = div.append('svg') 53 | .attr('width', width) 54 | .attr('height', height + margin) 55 | .append('g') 56 | .attr('transform', 'translate(0, 0)'); 57 | 58 | $scope.$on('change:vis', function () { 59 | _updateDimensions(); 60 | _buildVis(globalData); 61 | }); 62 | 63 | let sankey = d3.sankey() 64 | .nodeWidth(15) 65 | .nodePadding(10) 66 | .size([width, height]); 67 | 68 | let path = sankey.link(); 69 | 70 | sankey 71 | .nodes(energy.nodes) 72 | .links(energy.links) 73 | .layout(32); 74 | 75 | let link = svg.append('g').selectAll('.link') 76 | .data(energy.links) 77 | .enter().append('path') 78 | .attr('class', 'link') 79 | .attr('d', path) 80 | .style('stroke-width', function (d) { 81 | return Math.max(1, d.dy); 82 | }) 83 | .sort(function (a, b) { 84 | return b.dy - a.dy; 85 | }); 86 | 87 | link.append('title') 88 | .text(function (d) { 89 | return d.source.name + ' → ' + d.target.name + '\n' + d.value; 90 | }); 91 | 92 | let node = svg.append('g').selectAll('.node') 93 | .data(energy.nodes) 94 | .enter().append('g') 95 | .attr('class', 'node') 96 | .attr('transform', function (d) { 97 | return 'translate(' + d.x + ',' + d.y + ')'; 98 | }) 99 | .call(d3.behavior.drag() 100 | .origin(function (d) { 101 | return d; 102 | }) 103 | .on('dragstart', function () { 104 | this.parentNode.appendChild(this); 105 | }) 106 | .on('drag', dragmove)); 107 | 108 | node.append('rect') 109 | .attr('height', function (d) { 110 | return d.dy; 111 | }) 112 | .attr('width', sankey.nodeWidth()) 113 | .style('fill', function (d) { 114 | d.color = color(d.name); 115 | return d.color; 116 | }) 117 | .style('stroke', function (d) { 118 | return d3.rgb(d.color).darker(2); 119 | }) 120 | .append('title') 121 | .text(function (d) { 122 | return d.name + '\n' + d.value; 123 | }); 124 | 125 | node.append('text') 126 | .attr('x', -6) 127 | .attr('y', function (d) { 128 | return d.dy / 2; 129 | }) 130 | .attr('dy', '.35em') 131 | .attr('text-anchor', 'end') 132 | .attr('transform', null) 133 | .text(function (d) { 134 | return d.name; 135 | }) 136 | .filter(function (d) { 137 | return d.x < width / 2; 138 | }) 139 | .attr('x', 6 + sankey.nodeWidth()) 140 | .attr('text-anchor', 'start'); 141 | 142 | function dragmove(d) { 143 | d3.select(svgRoot).attr('transform', 'translate(' + d.x + ',' + (d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))) + ')'); 144 | sankey.relayout(); 145 | link.attr('d', path); 146 | } 147 | }; 148 | 149 | $scope.$watch('esResponse', function (resp) { 150 | if (resp) { 151 | var data = sankeyAggResponse($scope.vis, resp); 152 | globalData = data; 153 | _buildVis(data); 154 | } 155 | }); 156 | }); 157 | -------------------------------------------------------------------------------- /public/lib/agg_response.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | import arrayToLinkedList from 'ui/agg_response/hierarchical/_array_to_linked_list'; 3 | 4 | module.exports = function sankeyProvider(Private, Notifier) { 5 | 6 | let notify = new Notifier({ 7 | location: 'Sankey chart response converter' 8 | }); 9 | 10 | let nodes = {}; 11 | let links = {}; 12 | let lastNode = -1; 13 | 14 | function processEntry(aggConfig, metric, aggData, prevNode) { 15 | _.each(aggData.buckets, function (b) { 16 | if (isNaN(nodes[b.key])) { 17 | nodes[b.key] = lastNode + 1; 18 | lastNode = _.max(_.values(nodes)); 19 | } 20 | if (aggConfig._previous) { 21 | var k = prevNode + 'sankeysplitchar' + nodes[b.key]; 22 | if (isNaN(links[k])) { 23 | links[k] = metric.getValue(b); 24 | } else { 25 | links[k] += metric.getValue(b); 26 | } 27 | } 28 | if (aggConfig._next) { 29 | processEntry(aggConfig._next, metric, b[aggConfig._next.id], nodes[b.key]); 30 | } 31 | }); 32 | } 33 | 34 | return function (vis, resp) { 35 | 36 | let metric = vis.aggs.bySchemaGroup.metrics[0]; 37 | let buckets = vis.aggs.bySchemaGroup.buckets; 38 | buckets = arrayToLinkedList(buckets); 39 | if (!buckets) { 40 | return { 41 | 'slices': { 42 | 'nodes': [], 43 | 'links': [] 44 | } 45 | }; 46 | } 47 | 48 | let firstAgg = buckets[0]; 49 | let aggData = resp.aggregations[firstAgg.id]; 50 | 51 | if (!firstAgg._next) { 52 | notify.error('need more than one sub aggs'); 53 | } 54 | 55 | nodes = {}; 56 | links = {}; 57 | lastNode = -1; 58 | 59 | processEntry(firstAgg, metric, aggData, -1); 60 | 61 | let invertNodes = _.invert(nodes); 62 | let chart = { 63 | 'slices': { 64 | 'nodes': _.map(_.keys(invertNodes), function (k) { 65 | return { 66 | 'name': invertNodes[k] 67 | }; 68 | }), 69 | 'links': _.map(_.keys(links), function (k) { 70 | let s = k.split('sankeysplitchar'); 71 | return { 72 | 'source': parseInt(s[0]), 73 | 'target': parseInt(s[1]), 74 | 'value': links[k] 75 | }; 76 | }) 77 | } 78 | }; 79 | 80 | return chart; 81 | }; 82 | }; 83 | -------------------------------------------------------------------------------- /releases/5.5/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esversion" : 6 3 | } 4 | -------------------------------------------------------------------------------- /releases/5.5/README.md: -------------------------------------------------------------------------------- 1 | # Kibana Sankey Diagram Plugin 2 | 3 | This is a sankey diagram visType plugin for Kibana 5.5+. 4 | 5 | This plugin was developped from . 6 | 7 | Here is an example: 8 | 9 | ![Sankey](sankey_5_5_Screenshot1.PNG) 10 | 11 | # Install 12 | 13 | ``` 14 | git clone https://github.com/JuanCarniglia/kbn_sankey_vis.git 15 | cd kbn_sankey_vis/releases/5.5 16 | npm install d3-sankey-plugin 17 | npm run build 18 | cp -R build/kbn_sankey_vis KIBANA_FOLDER_PATH/installedPlugins/ 19 | ``` 20 | 21 | ** Note that in NTFS file systems, file paths that exceed 260 characters will fail with cp, you have to use ROBOCOPY: 22 | 23 | ``` 24 | robocopy /S build/kbn_sankey_vis KIBANA_FOLDER_PATH/installedPlugins/kbn_sankey_vis 25 | ``` 26 | 27 | ** Also note that if npm run build fails, with a rsync.js error, it is likelly that you don't have RSYNC.EXE installed 28 | in your system, and also that you don't have it on your PATH environment variable. 29 | 30 | Install it from https://www.itefix.net/cwrsync and run: 31 | 32 | ``` 33 | set PATH=%PATH%;{rsync installation directory}\bin 34 | ``` 35 | 36 | # Uninstall 37 | 38 | ``` 39 | bin/kibana plugin --remove kbn_sankey_vis 40 | ``` 41 | -------------------------------------------------------------------------------- /releases/5.5/index.js: -------------------------------------------------------------------------------- 1 | module.exports = function (kibana) { 2 | return new kibana.Plugin({ 3 | name: 'kbn_sankey_vis', 4 | require: ['kibana', 'elasticsearch'], 5 | uiExports: { 6 | visTypes: [ 7 | 'plugins/kbn_sankey_vis/kbn_sankey_vis' 8 | ] 9 | } 10 | }); 11 | }; 12 | -------------------------------------------------------------------------------- /releases/5.5/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kbn_sankey_vis", 3 | "version": "kibana", 4 | "authors": [ 5 | "Chenryn " 6 | ], 7 | "description": "Sankey Diagram Visualization Plugin", 8 | "main": "index.js", 9 | "license": "Apache-2.0", 10 | "homepage": "https://github.com/JuanCarniglia/kbn_sankey_vis", 11 | "repository": "https://github.com/JuanCarniglia/kbn_sankey_vis.git", 12 | "scripts": { 13 | "test": "gulp test", 14 | "test:coverage": "gulp coverage", 15 | "start": "gulp dev", 16 | "precommit": "gulp lint", 17 | "build": "gulp build" 18 | }, 19 | "dependencies": { 20 | "d3-plugins-sankey": ">=1.1.0" 21 | }, 22 | "devDependencies": { 23 | "babel-eslint": "^4.1.8", 24 | "bluebird": "^3.3.1", 25 | "eslint": "^2.1.0", 26 | "eslint-plugin-mocha": "^2.0.0", 27 | "gulp": "^3.9.1", 28 | "gulp-eslint": "^2.0.0", 29 | "gulp-util": "^3.0.7", 30 | "gulp-zip": "3.1.0", 31 | "husky": "^0.10.2", 32 | "lodash": "^4.4.0", 33 | "minimist": "1.2.0", 34 | "mkdirp": "^0.5.1", 35 | "rimraf": "^2.4.4", 36 | "rsync": "^0.4.0", 37 | "d3-plugins-sankey": ">=1.1.0" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /releases/5.5/public/kbn_sankey_vis.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | -------------------------------------------------------------------------------- /releases/5.5/public/kbn_sankey_vis.js: -------------------------------------------------------------------------------- 1 | 2 | import 'ui/agg_table'; 3 | import 'ui/agg_table/agg_table_group'; 4 | 5 | import 'plugins/kbn_sankey_vis/kbn_sankey_vis.less'; 6 | import 'plugins/kbn_sankey_vis/kbn_sankey_vis_controller'; 7 | 8 | import { TemplateVisTypeProvider } from 'ui/template_vis_type/template_vis_type'; 9 | import { VisSchemasProvider } from 'ui/vis/schemas'; 10 | import kbnSankeyVisTemplate from 'plugins/kbn_sankey_vis/kbn_sankey_vis.html'; 11 | 12 | import { VisTypesRegistryProvider } from 'ui/registry/vis_types'; 13 | 14 | VisTypesRegistryProvider.register(KbnSankeyVisProvider); 15 | 16 | function KbnSankeyVisProvider(Private) { 17 | let TemplateVisType = Private(TemplateVisTypeProvider); 18 | let Schemas = Private(VisSchemasProvider); 19 | 20 | return new TemplateVisType({ 21 | name: 'kbn_sankey', 22 | title: 'Sankey Diagram', 23 | icon: 'fa-random', 24 | description: 'Sankey charts are ideal for displaying the material, energy and cost flows.', 25 | template: kbnSankeyVisTemplate, 26 | params: { 27 | defaults: { 28 | showMetricsAtAllLevels: false 29 | }, 30 | editor: '' 31 | }, 32 | hierarchicalData: function (vis) { 33 | return Boolean(vis.params.showPartialRows || vis.params.showMetricsAtAllLevels); 34 | }, 35 | schemas: new Schemas([{ 36 | group: 'metrics', 37 | name: 'metric', 38 | title: 'Split Size', 39 | min: 1, 40 | max: 1, 41 | defaults: [{ 42 | type: 'count', 43 | schema: 'metric' 44 | }] 45 | }, { 46 | group: 'buckets', 47 | name: 'segment', 48 | title: 'Split Slices', 49 | aggFilter: '!geohash_grid', 50 | min: 0, 51 | max: Infinity 52 | }]), 53 | requiresSearch: true 54 | }); 55 | } 56 | 57 | // export the provider so that the visType can be required with Private() 58 | export default KbnSankeyVisProvider; 59 | -------------------------------------------------------------------------------- /releases/5.5/public/kbn_sankey_vis.less: -------------------------------------------------------------------------------- 1 | .node rect { 2 | cursor: move; 3 | fill-opacity: .9; 4 | shape-rendering: crispEdges; 5 | } 6 | 7 | .node text { 8 | pointer-events: none; 9 | text-shadow: 0 1px 0 #fff; 10 | } 11 | 12 | .link { 13 | fill: none; 14 | stroke: #000; 15 | stroke-opacity: .2; 16 | } 17 | 18 | .link:hover { 19 | stroke-opacity: .5; 20 | } 21 | -------------------------------------------------------------------------------- /releases/5.5/public/kbn_sankey_vis_controller.js: -------------------------------------------------------------------------------- 1 | import { uiModules } from 'ui/modules'; 2 | 3 | const module = uiModules.get('kibana/kbn_sankey_vis', ['kibana']); 4 | 5 | import d3 from 'd3'; 6 | import _ from 'lodash'; 7 | import $ from 'jquery'; 8 | //import S from 'd3-plugins-sankey'; 9 | 10 | import AggResponseProvider from './lib/agg_response'; 11 | 12 | module.controller('KbnSankeyVisController', function ($scope, $element, $rootScope, Private) { 13 | const sankeyAggResponse = Private(AggResponseProvider); 14 | 15 | let svgRoot = $element[0]; 16 | let color = d3.scale.category10(); 17 | let margin = 20; 18 | let width; 19 | let height; 20 | let div; 21 | let svg; 22 | let globalData = null; 23 | 24 | let _updateDimensions = function _updateDimensions() { 25 | let delta = 10; 26 | let w = $(svgRoot).parent().width() - 10; 27 | let h = $(svgRoot).parent().height() - 40; 28 | if (w) { 29 | if (w > delta) { 30 | w -= delta; 31 | } 32 | width = w; 33 | } 34 | if (h) { 35 | if (h > delta) { 36 | h -= delta; 37 | } 38 | height = h; 39 | } 40 | }; 41 | 42 | 43 | let _buildVis = function (data) { 44 | _updateDimensions(); 45 | 46 | d3.select(svgRoot).selectAll('svg').remove(); 47 | 48 | let energy = data.slices; 49 | div = d3.select(svgRoot); 50 | if (!energy.nodes.length) return; 51 | 52 | svg = div.append('svg') 53 | .attr('width', width) 54 | .attr('height', height + margin) 55 | .append('g') 56 | .attr('transform', 'translate(0, 0)'); 57 | 58 | $scope.$on('change:vis', function () { 59 | _updateDimensions(); 60 | _buildVis(globalData); 61 | }); 62 | 63 | let sankey = d3.sankey() 64 | .nodeWidth(15) 65 | .nodePadding(10) 66 | .size([width, height]); 67 | 68 | let path = sankey.link(); 69 | 70 | sankey 71 | .nodes(energy.nodes) 72 | .links(energy.links) 73 | .layout(32); 74 | 75 | let link = svg.append('g').selectAll('.link') 76 | .data(energy.links) 77 | .enter().append('path') 78 | .attr('class', 'link') 79 | .attr('d', path) 80 | .style('stroke-width', function (d) { 81 | return Math.max(1, d.dy); 82 | }) 83 | .sort(function (a, b) { 84 | return b.dy - a.dy; 85 | }); 86 | 87 | link.append('title') 88 | .text(function (d) { 89 | return d.source.name + ' → ' + d.target.name + '\n' + d.value; 90 | }); 91 | 92 | let node = svg.append('g').selectAll('.node') 93 | .data(energy.nodes) 94 | .enter().append('g') 95 | .attr('class', 'node') 96 | .attr('transform', function (d) { 97 | return 'translate(' + d.x + ',' + d.y + ')'; 98 | }) 99 | .call(d3.behavior.drag() 100 | .origin(function (d) { 101 | return d; 102 | }) 103 | .on('dragstart', function () { 104 | this.parentNode.appendChild(this); 105 | }) 106 | .on('drag', dragmove)); 107 | 108 | node.append('rect') 109 | .attr('height', function (d) { 110 | return d.dy; 111 | }) 112 | .attr('width', sankey.nodeWidth()) 113 | .style('fill', function (d) { 114 | d.color = color(d.name); 115 | return d.color; 116 | }) 117 | .style('stroke', function (d) { 118 | return d3.rgb(d.color).darker(2); 119 | }) 120 | .append('title') 121 | .text(function (d) { 122 | return d.name + '\n' + d.value; 123 | }); 124 | 125 | node.append('text') 126 | .attr('x', -6) 127 | .attr('y', function (d) { 128 | return d.dy / 2; 129 | }) 130 | .attr('dy', '.35em') 131 | .attr('text-anchor', 'end') 132 | .attr('transform', null) 133 | .text(function (d) { 134 | return d.name; 135 | }) 136 | .filter(function (d) { 137 | return d.x < width / 2; 138 | }) 139 | .attr('x', 6 + sankey.nodeWidth()) 140 | .attr('text-anchor', 'start'); 141 | 142 | function dragmove(d) { 143 | d3.select(svgRoot).attr('transform', 'translate(' + d.x + ',' + (d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))) + ')'); 144 | sankey.relayout(); 145 | link.attr('d', path); 146 | } 147 | }; 148 | 149 | $scope.$watch('esResponse', function (resp) { 150 | if (resp) { 151 | var data = sankeyAggResponse($scope.vis, resp); 152 | globalData = data; 153 | if (data && data.slices){ 154 | _buildVis(data); 155 | } 156 | } 157 | }); 158 | }); 159 | -------------------------------------------------------------------------------- /releases/5.5/public/lib/agg_response.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | 3 | import { arrayToLinkedList } from 'ui/agg_response/hierarchical/_array_to_linked_list'; 4 | 5 | module.exports = function sankeyProvider(Private, Notifier) { 6 | 7 | let notify = new Notifier({ 8 | location: 'Sankey chart response converter' 9 | }); 10 | 11 | let nodes = {}; 12 | let links = {}; 13 | let lastNode = -1; 14 | 15 | function processEntry(aggConfig, metric, aggData, prevNode) { 16 | _.each(aggData.buckets, function (b) { 17 | if (isNaN(nodes[b.key])) { 18 | nodes[b.key] = lastNode + 1; 19 | lastNode = _.max(_.values(nodes)); 20 | } 21 | if (aggConfig._previous) { 22 | var k = prevNode + 'sankeysplitchar' + nodes[b.key]; 23 | if (isNaN(links[k])) { 24 | links[k] = metric.getValue(b); 25 | } else { 26 | links[k] += metric.getValue(b); 27 | } 28 | } 29 | if (aggConfig._next) { 30 | processEntry(aggConfig._next, metric, b[aggConfig._next.id], nodes[b.key]); 31 | } 32 | }); 33 | } 34 | 35 | return function (vis, resp) { 36 | 37 | let chart = {}; 38 | 39 | let metric = vis.aggs.bySchemaGroup.metrics[0]; 40 | if (vis.aggs && vis.aggs.bySchemaGroup.buckets && vis.aggs.bySchemaGroup.buckets.length > 0) { 41 | let buckets = vis.aggs.bySchemaGroup.buckets; 42 | buckets = arrayToLinkedList(buckets); 43 | 44 | if (!buckets) { 45 | return { 46 | 'slices': { 47 | 'nodes': [], 48 | 'links': [] 49 | } 50 | }; 51 | } else { 52 | let firstAgg = buckets[0]; 53 | let aggData = resp.aggregations[firstAgg.id]; 54 | 55 | if (!firstAgg._next) { 56 | notify.error('need more than one sub aggs'); 57 | } 58 | 59 | nodes = {}; 60 | links = {}; 61 | lastNode = -1; 62 | 63 | processEntry(firstAgg, metric, aggData, -1); 64 | 65 | let invertNodes = _.invert(nodes); 66 | chart = { 67 | 'slices': { 68 | 'nodes': _.map(_.keys(invertNodes), function (k) { 69 | return { 70 | 'name': invertNodes[k] 71 | }; 72 | }), 73 | 'links': _.map(_.keys(links), function (k) { 74 | let s = k.split('sankeysplitchar'); 75 | return { 76 | 'source': parseInt(s[0]), 77 | 'target': parseInt(s[1]), 78 | 'value': links[k] 79 | }; 80 | }) 81 | } 82 | }; 83 | } 84 | } 85 | 86 | return chart; 87 | }; 88 | }; 89 | -------------------------------------------------------------------------------- /releases/5.5/sankey_5_5_Screenshot1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuanCarniglia/kbn_sankey_vis/504f3e93af58bb3c0c52df0ed6252dacbca30229/releases/5.5/sankey_5_5_Screenshot1.PNG -------------------------------------------------------------------------------- /sankey_5_5_Screenshot1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuanCarniglia/kbn_sankey_vis/504f3e93af58bb3c0c52df0ed6252dacbca30229/sankey_5_5_Screenshot1.PNG --------------------------------------------------------------------------------