Repository: incubator-senssoft-tap Updated Branches: refs/heads/ryan-sankey 855c20594 -> dd5f0fea2
Added left-alignment sankey modifications and styling updates Project: http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/commit/dd5f0fea Tree: http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/tree/dd5f0fea Diff: http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/diff/dd5f0fea Branch: refs/heads/ryan-sankey Commit: dd5f0fea21d27172356afc8d07a06c36c358b1db Parents: 855c205 Author: Arthi Vezhavendan <arthi...@gmail.com> Authored: Wed Sep 13 13:56:37 2017 -0400 Committer: Arthi Vezhavendan <arthi...@gmail.com> Committed: Wed Sep 13 13:56:37 2017 -0400 ---------------------------------------------------------------------- public/components/AppResults.jsx | 75 +++++---- public/components/visualizations/SankeyPlot.jsx | 154 +++++++++++++++---- 2 files changed, 166 insertions(+), 63 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/dd5f0fea/public/components/AppResults.jsx ---------------------------------------------------------------------- diff --git a/public/components/AppResults.jsx b/public/components/AppResults.jsx index 163e753..4b9a9ac 100644 --- a/public/components/AppResults.jsx +++ b/public/components/AppResults.jsx @@ -27,7 +27,7 @@ class AppResults extends Component { super(props); this.state = { result : 'counts', - metric : 'out_degree', + metric : 'click', //educationlevels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], //gender : 0, ab : false, @@ -91,24 +91,37 @@ class AppResults extends Component { //console.log(this.props.app); const { name, results } = this.props.app; - - - // get graph data from distill - // var url = 'http://distill:8090/sankey/userale?from=now-15m&to=now&size=20'; //snarl-distill calls - // //var url = 'http://localhost:8090'; - // axios.get(url) - // .then( (response) => { - // //console.log("response", response); - // var sankeyhtml = response.data; - // // this.setState({ - // // fetchUser: response.data - // // }); - // //console.log("fetchUser", this.state.fetchUser); - // }) - // .catch( (error) => { - // console.log(error); - // }); - var sankeyhtml = ""; + console.log("RESULTS:"); + console.log(name); + console.log(results); + + + //get graph data from distill + //var url = 'http://distill:8090/sankey/userale?from=now-15m&to=now&size=20'; //snarl-distill calls + var url = 'http://vlsmsbx.draper.com:8090/sankey/userale-js?from=now-25d&to=now-10d&size=10&event='+this.state.metric;//mouseover,click' + //var url = 'http://localhost:8090'; + axios.get(url) + .then( (response) => { + //console.log("response", response); + //var sankeyhtml = response.data; + console.log(url); + console.log("RESPONSE DATA FROM DISTILL"); + console.log(response.data); + console.log("Filters:" + this.state.metric); + this.props.app.results.counts = response.data.histogram; + this.props.app.results.sankey = { + nodes : response.data.nodes, + links : response.data.links, + }; + // this.setState({ + // fetchUser: response.data + // }); + //console.log("fetchUser", this.state.fetchUser); + }) + .catch( (error) => { + console.log(error); + }); + //var sankeyhtml = ""; // var url = 'http://distill:8090/sankey/userale?from=now-15m&to=now&size=20'; // var url = 'http://localhost:8090'; @@ -149,32 +162,32 @@ class AppResults extends Component { <div className='grouped fields'> <div className='field'> <div className='ui radio checkbox'> - <input type='radio' name='metric' value='out_degree' defaultChecked></input> - <label>Out Degree</label> + <input type='radio' name='metric' value='mouseover' defaultChecked></input> + <label>Mouseover</label> </div> </div> <div className='field'> <div className='ui radio checkbox'> - <input type='radio' name='metric' value='in_degree'></input> - <label>In Degree</label> + <input type='radio' name='metric' value='click'></input> + <label>Click</label> </div> </div> <div className='field'> <div className='ui radio checkbox'> - <input type='radio' name='metric' value='betweenness_cent_dir_weighted'></input> - <label>Weighted Betweenness</label> + <input type='radio' name='metric' value='blur'></input> + <label>Blur</label> </div> </div> <div className='field'> <div className='ui radio checkbox'> - <input type='radio' name='metric' value='closeness_cent_dir_weighted'></input> - <label>Weighted Closeness</label> + <input type='radio' name='metric' value='focus'></input> + <label>Focus</label> </div> </div> <div className='field'> <div className='ui radio checkbox'> - <input type='radio' name='metric' value='closeness_cent_dir_unweighted'></input> - <label>Unweighted Closeness</label> + <input type='radio' name='metric' value='other'></input> + <label>Other</label> </div> </div> </div> @@ -309,14 +322,14 @@ class AppResults extends Component { case 'sankey': return ( <div> - <SankeyPlot filters={this.state} data={results.graph} element='sankey-plot-viz' metric={this.state.metric} /> + <SankeyPlot filters={this.state} data={results.sankey} element='sankey-plot-viz' metric={this.state.metric} /> </div> ); case 'counts': default: return ( <div> - <SankeyPlot filters={this.state} data={results.graph} element='sankey-plot-viz' metric={this.state.metric} /> + <SankeyPlot filters={this.state} data={results.sankey} element='sankey-plot-viz' metric={this.state.metric} /> <Counts filters={this.state} data={results.counts} /> </div> ); http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/dd5f0fea/public/components/visualizations/SankeyPlot.jsx ---------------------------------------------------------------------- diff --git a/public/components/visualizations/SankeyPlot.jsx b/public/components/visualizations/SankeyPlot.jsx index 6d7e47b..a765ce9 100644 --- a/public/components/visualizations/SankeyPlot.jsx +++ b/public/components/visualizations/SankeyPlot.jsx @@ -17,6 +17,7 @@ import React, { Component, PropTypes } from 'react'; import * as d3 from 'd3'; //import {'d3-interpolate'} from 'd3'; //require('../../d3sankey.js').default; +//import sankey as sankeynow from '/d3-sankey.js'; const colors_old = ['#A7003C', '#00A76B', '#0090A7', '#003DA7', '#6B00A7']; const colors_new = ['#d45d35', '#DBA915', '#BFD02C', '#38A6D8', '#852EB7']; @@ -52,14 +53,17 @@ class SankeyPlot extends Component { bottom : 20, left : 20, }; - this.fullWidth = 600; - this.fullHeight = 300; + this.fullWidth = 800; + this.fullHeight = 400; this.width = this.fullWidth - this.margin.left - this.margin.right; this.height = this.fullHeight - this.margin.top - this.margin.bottom; this.formatNumber = d3.format(',.0f'); this.format = d => `${this.formatNumber(d)} TWh`; + //this.mainRadius = 280; + + //this.update(); this.color = d3.scaleOrdinal() .range(colors_old); @@ -68,7 +72,8 @@ class SankeyPlot extends Component { .attr('width', this.fullWidth) .attr('height', this.fullHeight) .append('g'); - + //.attr('transform', `translate(${this.margin.left + this.width / 2},${this.margin.top + this.height / 2})`); + this.sankey = sankey() .nodeWidth(15) .nodePadding(10) @@ -86,50 +91,76 @@ class SankeyPlot extends Component { // D3 render update() { - //let data = this.props.data[this.props.metric]; - let data = require('../../sankey_example.js').default; - console.log("data in update = " + data); - console.log("nodes: "+ data[0].nodes.length); - console.log("links: "+ data[1].links.length); + + let data = this.props.data;//[this.props.metric]; + //if (data == null) { + //let data = require('../../sankey_example.js').default; + //}; + console.log("SANKEY PROPS DATA"); + console.log(this.props.data); + // console.log("data in update = " + data); + // console.log("nodes: "+ data[0].nodes.length); + // console.log("links: "+ data[1].links.length); + + this.sankey - .nodes(data[0].nodes) - .links(data[1].links) + .nodes(data.nodes) + .links(data.links) .layout(32); - const link = this.svg.append('g').selectAll('.link') - .data(data[1].links) + d3.selectAll(".linkSankey").remove(); + d3.selectAll(".nodeSankey").remove(); + + const link = this.svg.append('g').selectAll('.linkSankey') + .data(data.links) .enter().append('path') .attr('class', 'linkSankey') .attr('d', this.path) - .style('stroke-width', d => Math.max(100, d.dy)) + .style('stroke-width', d => d.dy) //d => Math.max(50, d.dy)) .style('fill', 'none') .style('stroke', "#000") .style('stroke-opacity', .2) .sort((a, b) => b.dy - a.dy); link.append('title') - .text(d => `${d.source.name} â ${d.target.name}\n${this.format(d.value)}`); + .text(d => `${d.source.name} â ${d.target.name}\n${d.value}`); - const node = this.svg.append('g').selectAll('.node') - .data(data[0].nodes) + const node = this.svg.append('g').selectAll('.nodeSankey') + .data(data.nodes) .enter().append('g') - .attr('class', 'node') + .attr('class', 'nodeSankey') .attr('transform', d => `translate(${d.x},${d.y})`) .call(d3.drag() .subject(d => d) .on('start', function() { this.parentNode.appendChild(this); }) - .on('drag', this.dragmove(d => d))); + //.on('drag', this.dragmove())); + .on('drag', function(d) { + // console.log("YAY DRAG"); + // console.log(d.x); + // console.log(d.y); + // console.log(d3.event.y); + // d3.select(this).attr('transform', d => `translate(${d.x},50)`);//${d.y = Math.max(0, Math.min(this.fullHeight - d.dy, d3.event.y))})`); + // this.sankey.relayout(); + // link.attr('d', this.path); + })); + + // console.log("rect size"); + // console.log(d => d.dy); node.append('rect') .attr('height', d => d.dy) .attr('width', this.sankey.nodeWidth()) .style('fill', d => d.color = this.color(d.name.replace(/ .*/, ''))) .style('stroke', d => d3.rgb(d.color).darker(2)) - .append('title') - .text(d => `${d.name}\n${this.format(d.value)}`); + .append('title'); + //.text(d => `${d.name}`); + + // node.append('title') + // .text(d => `${d.name}`);// â ${d.target.name}\n${d.value}`); + node.append('text') .attr('x', -6) @@ -137,7 +168,7 @@ class SankeyPlot extends Component { .attr('dy', '.35em') .attr('text-anchor', 'end') .attr('transform', null) - .text(d => d.name) + .text(d => d.name[0,9]) .filter(d => d.x < this.width / 2) .attr('x', 6 + this.sankey.nodeWidth()) .attr('text-anchor', 'start'); @@ -145,21 +176,21 @@ class SankeyPlot extends Component { } dragmove(d) { - //TODO: fix dragmove: the function is called unnecessarily and doens't work + console.log("DRAGGING HAPPENED");// - dragmove called unnecessarily - todo: fix"); // d3.select(this).attr('transform', `translate(${d.x},${d.y = Math.max(0, Math.min(this.height - d.dy, d3.event.y))})`); // sankey.relayout(); // link.attr('d', this.path); } hideTooltip() { - //console.log("HIDE TOOLTIP HAPPENED - todo: verify"); + console.log("HIDE TOOLTIP HAPPENED - todo: verify"); this.tooltip.transition() .duration(350) .style('opacity', 0); } showTooltip(activity, x, y) { - //console.log("SHOW TOOLTIP HAPPENED - todo: verify"); + console.log("SHOW TOOLTIP HAPPENED - todo: verify"); this.tooltip.transition() .duration(350) .style('opacity', 0.9); @@ -170,18 +201,18 @@ class SankeyPlot extends Component { .html(`Action: ${activity.action}<br>Id: ${activity.elementId}<br>Group: ${activity.elementGroup}`); } -} - // d3-sankey layout taken from: <<>> and modified by Ryan function sankey() { - var sankey = {}, + var sankey = {}, nodeWidth = 24, - nodePadding = 8, + nodePadding = 20, size = [1, 1], + align = 'left', nodes = [], links = []; + sankey.nodeWidth = function(_) { if (!arguments.length) return nodeWidth; @@ -276,7 +307,9 @@ function sankey() { ); }); } - + sankey.nodeAlign = function(_) { + return arguments.length ? (align = typeof _ === "function" ? _ : constant(_), sankey) : align; + }; // Iteratively assign the breadth (x-position) for each node. // Nodes are assigned the maximum breadth of incoming neighbors plus one; // nodes with no incoming links are assigned breadth zero, while @@ -301,10 +334,42 @@ function sankey() { ++x; } - // - moveSinksRight(x); + // if (reverse) { + // // Flip nodes horizontally + // nodes.forEach(function(node) { + // node.x *= -1; + // node.x += x - 1; + // }); + // } + + if (align === 'center') { + moveSourcesRight(); + } + if (align === 'justify') { + moveSinksRight(x); + } + scaleNodeBreadths((size[0] - nodeWidth) / (x - 1)); } + + function moveSourcesRight() { + nodes.slice() + // Pack nodes from right to left + .sort(function(a, b) { return b.x - a.x; }) + .forEach(function(node) { + if (!node.targetLinks.length) { + node.x = d3Array.min(node.sourceLinks, function(d) { return d.target.x; }) - 1; + } + }); + } + + function moveSinksRight(x) { + nodes.forEach(function(node) { + if (!node.sourceLinks.length) { + node.x = x - 1; + } + }); + } function moveSourcesRight() { nodes.forEach(function(node) { @@ -471,13 +536,38 @@ function sankey() { return link.value; } + sankey.align = function(_) { + if (!arguments.length) return align; + align = _.toLowerCase(); + return sankey; + }; + return sankey; -} + } + + SankeyPlot.propTypes = { element : PropTypes.string.isRequired, data : PropTypes.object, metric : PropTypes.string.isRequired, + // data : PropTypes.shape({ + // inMatrix : PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.number)), + // outMatrix : PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.number)), + // in : PropTypes.arrayOf(PropTypes.shape({ + // index : PropTypes.number, + // name : PropTypes.string, + // })), + // out : PropTypes.arrayOf(PropTypes.shape({ + // index : PropTypes.number, + // name : PropTypes.string, + // })), + // between : PropTypes.arrayOf(PropTypes.shape({ + // index : PropTypes.number, + // name : PropTypes.string, + // value : PropTypes.number, + // })), + // }).isRequired, };