Have to save text height in d using d3 library - javascript

I am trying to save text height value in d (so I can draw around appropriate size rectangle), but it's not defined in tick function or any where else.
d.height
should hold values. (but it doesn't)
So I have tick function (part of it):
function tick() {
z.attr('d', function(d) {
alert (d.height); //not defined
var sourceX = d.source.x + textWidth/2 + 10,
sourceY = d.source.y + d.height/2 + 10,
targetX = d.target.x + textWidth/2 + 10,
targetY = d.target.y + d.height/2 + 10;
return 'M' + sourceX + ',' + sourceY + 'L' + targetX + ',' + targetY;
});
shape.attr('transform', function(d) {
middle_rect.attr("height", d.height/2 + 20);
side_rect.attr("height", d.height/2 + 20);
return 'translate(' + d.x + ',' + d.y + ')';
});
And here how I'm trying to save values (bottom of code)
shape = shape.data(nodes);
// add new nodes
var g = shape.enter().append('svg:g').attr('class', function(d) { return d.type +' node'; });
middle_rect = svg.selectAll(".middle")
.append("svg:rect")
.attr("rx", "10")
.attr("ry", "10")
.style("stroke", "rgb(47,136,214)")
.style("fill", "url(#middle_gradient)");
side_rect = svg.selectAll(".side")
.append("svg:rect")
.attr("rx", "10")
.attr("ry", "10")
.style("stroke", "rgb(47,136,214)")
.style("fill", "url(#side_gradient)");
txt = g.append('svg:text')
.attr('class',function (d){return 'bigest ' + d.id ;} )
.attr('y', ".5em")
.attr("dy", "1em")
.style("fill", "black")
.each (function (d) {
d.height = this.getBoundingClientRect().height;
// alert (d.height); //here I have this value
});
Can I make it global? Or have to save these values?

It appears that you are trying to attach a height field to the Node itself, which is considered a bad practice in general.
Why not create a data structure to store all these values, or possibly even add the height to your dataset.
Another approach could be creating a data-height attribute on the text node.
http://jsfiddle.net/heavyhorse/893jT/
svg.selectAll('text.data-label').each(function(d, i){
var height = this.getBoundingClientRect().height;
var width = this.getBoundingClientRect().width;
console.log(i+': ('+width+','+height+')');
// create custom data-attr:
this.setAttribute('data-width', width);
this.setAttribute('data-height', height);
console.log(this);
// attach to dataset:
dataset[i].width = width;
dataset[i].height = height;
});

Related

d3 drag blurring/jittery handle

This is on d3 v4.
I'm trying to create an expandable rectangle area, with bounds (sort of a constrained d3-brush). I add a handle which shows up on mouseover.
var rectHeight = 80, rectWidth = 100, maxWidth = 200;
var svg = d3.select("svg");
var brect = svg.append("g")
.attr("id", "brect");
brect.append("rect")
.attr("id", "dataRect")
.attr("width", rectWidth)
.attr("height", rectHeight)
.attr("fill", "green");
var handleResizeGroup = brect.append("g")
.attr("id", "handleResizeGroup")
.attr("transform", `translate(${rectWidth})`)
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded));
function dragStarted() {
d3.select(this.previousSibling).classed("active", true);
}
function dragEnded() {
d3.select(this.previousSibling).classed("active", false);
}
function dragged(d) {
var h = d3.select(this);
var r = d3.select(this.previousSibling);
var currWidth = r.attr("width");
var t = (d3.event.x >= 0 && d3.event.x <= maxWidth) ? d3.event.x : currWidth;
r.attr("width", t);
h.attr("transform", `translate(${t})`)
}
handleResizeGroup.append("path")
.attr("fill-opacity", 0)
.attr("stroke-opacity", 0)
.attr("stroke", "grey")
.attr("cursor", "ew-resize")
.attr("d", resizePath);
handleResizeGroup.append("rect")
.attr("id", "resizeRect")
.attr("width", "8")
.attr("fill-opacity", 0)
.attr("cursor", "ew-resize")
.attr("height", rectHeight)
//.attr("pointer-events", "all")
.on("mouseover", function(){
d3.select(this.previousSibling)
.attr("stroke-opacity", "100%");
})
.on("mouseout", function() {
d3.select(this.previousSibling)
.attr("stroke-opacity", "0");
});
function resizePath(d) {
var e = 1,
x = e ? 1 : -1,
y = rectHeight / 3;
return "M" + (.5 * x) + "," + y
+ "A6,6 0 0 " + e + " " + (6.5 * x) + "," + (y + 6)
+ "V" + (2 * y - 6)
+ "A6,6 0 0 " + e + " " + (.5 * x) + "," + (2 * y)
+ "Z"
+ "M" + (2.5 * x) + "," + (y + 8)
+ "V" + (2 * y - 8)
+ "M" + (4.5 * x) + "," + (y + 8)
+ "V" + (2 * y - 8);
}
rect.active {
stroke-width: 1;
stroke: rgb(0,0,0);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width=1200 height=500></svg>
I'm noticing 2 issues
When I drag the handle, I see a jitter in the handle itself (presumably because handle is shown only on mouseover?)
If I drag the mouse too fast - say to the left, the rectangle does not catch-up
Can someone help to understand what's going on, and how to fix these?
Thank you!
For your first issue, add .attr('pointer-events', 'none') to the greenrect`. the handle is jittering because your cursor moves slightly faster than the handle does- so you're constantly mousing-out of and then mousing-in to the handle as you move and it catches up.
I don't really see what you're doing with resizePath. Is that adding the grey stroke around the rect? Why not just add/remove a stroke? Your second issue may be due to constantly resizing that path.

D3 force layout graph zoom functionality

I new in js and can't solve problem i faced by myself, so I will be glad for any advices or helps.
In my project I created force layout graph.
What I should do is to add zoom in and zoom out functionality for my graph.
I created little example to show what problem I faced.
function myGraph(el){
this.addNode = function (id, category, opened, parentT, name, nodeType, countLinks, dob, country, childsCount) {
var parent = [];
parent.push(parentT);
nodes.push({ "id": id, "category": category, "opened": opened, "parent": parent, "name": name, "nodeType": nodeType, "countLinks": countLinks, "dob": dob, "country": country, "childCount": childsCount });
update();
}
this.addLink = function (sourceId, targetId, type) {
var sourceNode = findNode(sourceId);
var targetNode = findNode(targetId);
if ((sourceNode !== undefined) && (targetNode !== undefined)) {
links.push({ "source": sourceNode, "target": targetNode, "type": type });
update();
}
}
var findNode = function (id) {
for (var i = 0; i < nodes.length; i++) {
if (nodes[i].id === id)
return nodes[i]
};
}
var w = 360,
h = 200;
var vis = this.vis = d3.select(el).append("svg:svg")
.attr("id", "mySvg")
.attr("width", w)
.attr("height", h)
.attr("viewBox", "0 0 " + w + " " + h)
.call(d3.behavior.zoom().scaleExtent([-0.5, 1]).on("zoom", redraw))
var g = vis.append('svg:g')
function redraw(e) {
if (!e) e = window.event;
if (e.type == "wheel" || e.shiftKey == true) {
g.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
}
var force = d3.layout.force()
.gravity(0.05)
.distance(100)
.charge(-200)
.size([w, h]);
var nodes = force.nodes(),
links = force.links();
var hiddenType = [];
g.append("g").attr("class", "links");
g.append("g").attr("class", "nodes");
var update = function () {
for (var i=0; i<links.length; i++) {
if (i != 0 &&
links[i].source == links[i-1].source &&
links[i].target == links[i-1].target) {
links[i].linknum = links[i-1].linknum + 1;
}
else {links[i].linknum = 1;};
};
var myLink = g.select(".links").selectAll(".link");
myLink.remove();
var link = g.select(".links").selectAll(".link")
.data(links);
link.enter().append("polyline")
.attr("class", "link")
.attr("type", function (d) { return "link_" + d.type; })
link.exit().remove();
var node = g.select(".nodes").selectAll(".node")
.data(nodes, function (d) { return d.id; })
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("links", function (d) { return d.countLinks })
.attr("type", function (d) { return d.nodeType })
.on("mousedown", mousedown)
.call(force.drag);
nodeEnter.append("circle")
.attr("r", "10px")
.attr("x", "-8px")
.attr("y", "-8px")
.attr("width", "16px")
.attr("height", "16px")
.style("fill", "white")
.style("stroke", "#ccc")
nodeEnter.append("rect")
.attr("pointer-events", "none")
.attr("class", "shape")
.attr("width", "250px")
.attr("height", "150px")
.style("fill", "transparent")
nodeEnter.append("text")
.attr("pointer-events", "none")
.attr("class", function (d) { return "nodetext_" + d.id })
.attr("y", "-10")
.text(function (d) { return d.id })
nodeEnter.append("title")
.text(function (d) { return d.id });
node.exit().remove();
function mousedown(d) {
d.fixed = true;
}
force.on("tick", function () {
link.attr("points", function(d){
if (d.source.y > d.target.y) {
return d.source.x + "," + d.source.y + " " + (d.source.x + ((d.target.x - d.source.x) / 2)) + "," + (d.target.y + ((d.source.y - d.target.y) / 2) + (15 * (d.linknum - 1))) + " " + d.target.x + "," + d.target.y;
}
if (d.source.y < d.target.y) {
return d.source.x + "," + d.source.y + " " + (d.source.x + ((d.target.x - d.source.x) / 2)) + "," + (d.source.y + ((d.target.y - d.source.y) / 2) + (15 * (d.linknum - 1))) + " " + d.target.x + "," + d.target.y;
}
})
node.attr("transform", function (d) {
if (d.fixed !== true) {
return "translate(" + d.x + "," + d.y + ")";
}
else {
return "translate(" + d.px + "," + d.py + ")";
}
});
});
force.start();
}
update();
}
graph = new myGraph("#graph");
graph.addNode("A")
graph.addNode("B")
graph.addNode("C")
graph.addLink("A", "B")
graph.addLink("A", "C")
https://jsfiddle.net/IMykytiv/nsxL8c52/
So problem is:
In my graph users can drag nodes in any direction, after that node's position becomes fixed. When I added zooming I can't drag nodes because zooming works first, so I added condition that zoom works only when shift key is pressed. So now I can both drag nodes and zoom.
But now I faced problem that if I drag node and try to zoom with mouse wheel it changed transform not from mouse position but from another point and I can't understand why. If I remove shift key condition zooming works as expected.
I finaly found solution and it was preaty easy.
Instead of adding condition with shift key pressed all I need to do is to prevent trigger zoom function on start draggin node event:
var drag = force.drag()
.on("dragstart", function (d) {
d3.event.sourceEvent.stopPropagation();
});
Updated jsfiddle here:
https://jsfiddle.net/IMykytiv/nsxL8c52/2/
Hope that it will be useful for anyone :)

Creating links between nodes d3.js currently returning Uncaught TypeError: Cannot read property 'weight' of undefined

I have the following json structure:
data = {
"nodes":[
{
"nodeType":"File",
"nodeId":16392,
"property1":"coint_ctolocal_partitions",
"property2":null,
"group":0,
"more":false
},
{
"nodeType":"File",
"nodeId":16386,
"property1":"pers_contrato_partitions",
"property2":null,
"group":0,
"more":false
}
],
"links":[
{
"source":16386,
"target":16392,
"value":0,
"val":"{\"Contract\":[\"Insurance\"]}",
"type":"PTN"
}
]
};
Currently this is returning Uncaught TypeError: Cannot read property 'weight' of undefined.
In our test data we simply used source:0 and target:1 and this works as I assume it is using the index of nodes to link the objects together. Or perhaps the linkindex function.
The full function looks like this:
returnTableRelationshipData = function(){
data = {} // json data as described above
// used to store the number of links between two nodes.
// mLinkNum[data.links[i].source + "," + data.links[i].target] = data.links[i].linkindex;
var mLinkNum = {};
// sort links first
sortLinks();
// set up linkIndex and linkNumer, because it may possible multiple links share the same source and target node
setLinkIndexAndNum();
// check that we don't have empty or null values
checkDataNotEmpty();
var w = 600,
h = 500;
var force = d3.layout.force()
.nodes(d3.values(data.nodes))
.links(data.links)
.size([w, h])
.linkDistance(150)
.charge(-300)
.on("tick", tick)
.start();
var svg = d3.select(".graphContainer").append("svg:svg")
.attr("width", w)
.attr("height", h);
var path = svg.append("svg:g")
.selectAll("path")
.data(force.links())
.enter().append("svg:path")
.attr("class", "link");
var circle = svg.append("svg:g")
.selectAll("circle")
.data(force.nodes())
.enter().append("svg:circle")
.attr("r", 6)
.call(force.drag);
var text = svg.append("svg:g")
.selectAll("g")
.data(force.nodes())
.enter().append("svg:g");
// A copy of the text with a thick white stroke for legibility.
text.append("svg:text")
.attr("x", 12)
.attr("y", ".31em")
.attr("class", "shadow")
.text(function(d){ return d.property1; });
text.append("svg:text")
.attr("x", 12)
.attr("y", ".31em")
.text(function(d){ return d.property1; });
// Use elliptical arc path segments to doubly-encode directionality.
function tick() {
path.attr("d", function(d){
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
dr = Math.sqrt(dx * dx + dy * dy);
// get the total link numbers between source and target node
var lTotalLinkNum = mLinkNum[d.source.id + "," + d.target.id] || mLinkNum[d.target.id + "," + d.source.id];
if(lTotalLinkNum > 1)
{
// if there are multiple links between these two nodes, we need generate different dr for each path
dr = dr/(1 + (1/lTotalLinkNum) * (d.linkindex - 1));
}
// generate svg path
return "M" + d.source.x + "," + d.source.y +
"A" + dr + "," + dr + " 0 0 1," + d.target.x + "," + d.target.y +
"A" + dr + "," + dr + " 0 0 0," + d.source.x + "," + d.source.y;
});
circle.attr("transform", function(d){
return "translate(" + d.x + "," + d.y + ")";
});
text.attr("transform", function(d){
return "translate(" + d.x + "," + d.y + ")";
});
}
// sort the links by source, then target
function sortLinks(){
if(data.links != null){
data.links.sort(function(a,b){
if(a.source > b.source){
return 1;
}else if(a.source < b.source){
return -1;
}else{
if(a.target > b.target){
return 1;
}if(a.target < b.target){
return -1;
}else{
return 0;
}
}
});
}
}
//any links with duplicate source and target get an incremented 'linknum'
function setLinkIndexAndNum(){
for(var i = 0; i < data.links.length; i++){
if(i != 0 &&
data.links[i].source == data.links[i-1].source &&
data.links[i].target == data.links[i-1].target){
data.links[i].linkindex = data.links[i-1].linkindex + 1;
}else{
data.links[i].linkindex = 1;
}// save the total number of links between two nodes
if(mLinkNum[data.links[i].target + "," + data.links[i].source] !== undefined){
mLinkNum[data.links[i].target + "," + data.links[i].source] = data.links[i].linkindex;
}else{
mLinkNum[data.links[i].source + "," + data.links[i].target] = data.links[i].linkindex;
}
}
}
function checkDataNotEmpty(){
data.links.forEach(function(link, index, list) {
if (typeof link.source === 'undefined') {
console.log('undefined link', data.nodes[link.source]);
}
if (typeof link.target === 'undefined') {
console.log('undefined source', data.nodes[link.target]);
}
console.log(typeof link.source, typeof link.target);
});
}
}
$(document).ready(function(){
returnTableRelationshipData();
});
In the nodes data we have nodeId which identifies the node uniquely. We also have property1 which is a unique table name.
So I need some way to set the source and target between the existing data using custom json keys, if this is possible? (ie map nodeId to be source or target).
At first I thought I was getting issues with integers, strings and so on, but we can convert between these if it will help. (I mean I did try this but to no success).
I have made a fiddle here: https://jsfiddle.net/lharby/j1q1oLzL/1/
You can do it like this to convert the target /source id to it index in nodes array:
//find the node index
function find(f){
var i = -1
data.nodes.forEach(function(node, index){
if(node.nodeId == f)
i = index;
});
return i;
}
//set the source and target index
data.links.forEach(function(d){
d.source = find(d.source);
d.target = find(d.target);
});
The link source target has to be index of the node array as mentioned in the docs
working code here

How to add remove certain json arrays from dropdown

I have a sunburst which has following layers of data
world=>continents=>countries=>fuels
NOw I want to include names of elements only until countries and not names of fuels. With my code I can add names of all elements in the dropdown but not sure how to remove names of fuels from the dropdown.
Fiddle: http://jsfiddle.net/8wd2xt9n/14/
Full code:
var width = 960,
height = 700,
radius = Math.min(width, height) / 2;
var b = {
w: 130,
h: 30,
s: 3,
t: 10
};
var x = d3.scale.linear()
.range([0, 2 * Math.PI]);
var y = d3.scale.sqrt()
.range([0, radius]);
var changeArray = [100, 80, 60, 0, -60, -80, -100];
var colorArray = ["#67000d", "#b73e43", "#d5464a", "#f26256", "#fb876e", "#fca78e", "#fcbba1", "#fee0d2", "#fff5f0"];
var svg = d3.select("#diagram").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + (height / 2 + 10) + ")");
var partition = d3.layout.partition()
.value(function (d) {
return d.Total;
});
var arc = d3.svg.arc()
.startAngle(function (d) {
return Math.max(0, Math.min(2 * Math.PI, x(d.x)));
})
.endAngle(function (d) {
return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
})
.innerRadius(function (d) {
return Math.max(0, y(d.y));
})
.outerRadius(function (d) {
return Math.max(0, y(d.y + d.dy));
});
console.log(arc)
function checkIt(error, root) {
initializeBreadcrumbTrail();
//intilaize dropdown
if (error) throw error;
var g = svg.selectAll("g")
.data(partition.nodes(root))
.enter().append("g");
var path = g.append("path")
.attr("d", arc)
.style("fill", function (d) {
var color;
if (d.Total_Change > 100) {
color = colorArray[0]
} else if (d.Total_Change > 0 && d.Total_Change < 100) {
color = colorArray[1]
} else {
color = colorArray[2]
}
d.color = color;
return color
})
.on("click", click)
.on("mouseover", mouseover);
var tooltip = d3.select("body")
.append("div")
.attr("id", "tooltips")
.style("position", "absolute")
.style("background-color", "#fff")
.style("z-index", "10")
.style("visibility", "hidden");
g.on("mouseover", function (d) {
return tooltip.style("visibility", "visible")
.html("<div class=" + "tooltip_container>" + "<h4 class=" + "tooltip_heading>" + d.name.replace(/[_-]/g, " ") + "</h4>" + "<br>" + "<p> Emissions 2013:" + " " + "<br>" + d.Total + " " + "<span>in Million Tons</span></p>" + "<br>" + "<p> Change in Emissions: <span>" + (d.Total_Change / d.Total * 100).toPrecision(3) + "%" + "</span></p>" + "</div>");
})
.on("mousemove", function () {
return tooltip.style("top", (d3.event.pageY - 10) + "px").style("left", (d3.event.pageX + 10) + "px");
})
.on("mouseout", function () {
return tooltip.style("visibility", "hidden");
});
//creating a dropdown
var dropDown = d3.select("#dropdown_container")
.append("select")
.attr("class", "selection")
.attr("name", "country-list");
var nodeArr = partition.nodes(root);
var options = dropDown.selectAll("option")
.data(nodeArr)
.enter()
.append("option");
options.text(function (d) {
var prefix = new Array(d.depth + 1);
var dropdownValues = d.name.replace(/[_-]/g, " ");
return dropdownValues;
}).attr("value", function (d) {
var dropdownValues = d.name;
return dropdownValues;
});
// transition on click
function click(d) {
// fade out all text elements
console.log(d)
path.transition()
.duration(750)
.attrTween("d", arcTween(d))
.each("end", function (e, i) {
// check if the animated element's data e lies within the visible angle span given in d
if (e.x >= d.x && e.x < (d.x + d.dx)) {
// get a selection of the associated text element
var arcText = d3.select(this.parentNode).select("text");
// fade in the text element and recalculate positions
arcText.transition().duration(750)
.attr("opacity", 1)
.attr("transform", function () {
return "rotate(" + computeTextRotation(e) + ")"
})
.attr("x", function (d) {
return y(d.y);
});
}
});
};
d3.select(".selection").on("change", function changePie() {
var value = this.value;
var index = this.selectedIndex;
var dataObj = nodeArr[index];
path[0].forEach(function (p) {
var data = d3.select(p).data(); //get the data from the path
if (data[0].name === value) {
console.log(data)
click(data[0]);//call the click function
}
});
console.log(this.value + " :c " + dataObj["Iron and steel"] + " in " + (dataObj.parent && dataObj.parent.name));
});
};
d3.json("https://gist.githubusercontent.com/heenaI/cbbc5c5f49994f174376/raw/743b3964d1dcc0b005ec2b024414877a36b0bd33/data.json", checkIt);
d3.select(self.frameElement).style("height", height + "px");
// Interpolate the scales!
function arcTween(d) {
var xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]),
yd = d3.interpolate(y.domain(), [d.y, 1]),
yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius]);
return function (d, i) {
return i ? function (t) {
return arc(d);
} : function (t) {
x.domain(xd(t));
y.domain(yd(t)).range(yr(t));
return arc(d);
};
};
}
function initializeBreadcrumbTrail() {
// Add the svg area.
var trail = d3.select("#sequence").append("svg:svg")
.attr("width", width)
.attr("height", 50)
.attr("id", "trail");
// Add the label at the end, for the percentage.
trail.append("svg:text")
.attr("id", "endlabel")
.style("fill", "#000");
}
function breadcrumbPoints(d, i) {
var points = [];
points.push("0,0");
points.push(b.w + ",0");
points.push(b.w + b.t + "," + (b.h / 2));
points.push(b.w + "," + b.h);
points.push("0," + b.h);
if (i > 0) { // Leftmost breadcrumb; don't include 6th vertex.
points.push(b.t + "," + (b.h / 2));
}
return points.join(" ");
}
// Update the breadcrumb trail to show the current sequence and percentage.
function updateBreadcrumbs(nodeArray) {
// Data join; key function combines name and depth (= position in sequence).
var g = d3.select("#trail")
.selectAll("g")
.data(nodeArray, function (d) {
return d.name.replace(/[_-]/g, " ") + d.Total;
});
// Add breadcrumb and label for entering nodes.
var entering = g.enter().append("svg:g");
entering.append("svg:polygon")
.attr("points", breadcrumbPoints)
.style("fill", "#d3d3d3");
entering.append("svg:text")
.attr("x", (b.w + b.t) / 2)
.attr("y", b.h / 2)
.attr("dy", "0.35em")
.attr("text-anchor", "middle")
.text(function (d) {
return d.name.replace(/[_-]/g, " ");
});
// Set position for entering and updating nodes.
g.attr("transform", function (d, i) {
return "translate(" + i * (b.w + b.s) + ", 0)";
});
// Remove exiting nodes.
g.exit().remove();
// Now move and update the percentage at the end.
d3.select("#trail").select("#endlabel")
.attr("x", (nodeArray.length + 0.5) * (b.w + b.s))
.attr("y", b.h / 2)
.attr("dy", "0.35em")
.attr("text-anchor", "middle");
// Make the breadcrumb trail visible, if it's hidden.
d3.select("#trail")
.style("visibility", "");
}
function getAncestors(node) {
var path = [];
var current = node;
while (current.parent) {
path.unshift(current);
current = current.parent;
}
return path;
}
function mouseover(d) {
var sequenceArray = getAncestors(d);
updateBreadcrumbs(sequenceArray);
}
Code that creates dropdown
/creating a dropdown
var dropDown = d3.select("#dropdown_container")
.append("select")
.attr("class", "selection")
.attr("name", "country-list");
var nodeArr = partition.nodes(root);
var options = dropDown.selectAll("option")
.data(nodeArr)
.enter()
.append("option");
options.text(function (d) {
var prefix = new Array(d.depth + 1);
var dropdownValues = d.name.replace(/[_-]/g, " ");
return dropdownValues;
}).attr("value", function (d) {
var dropdownValues = d.name;
return dropdownValues;
});
Data structure can be viewed here
As you are grabbing the values of the dropdown menu from the nodes of the partition, you have the depth of the nodes at hand when setting up the dropdown, thus you can filter:
var nodeArr = partition.nodes(root);
var options = dropDown.selectAll("option")
.data(nodeArr.filter(function(d){return d.depth < 3;}))
.enter()
.append("option");
I hope that helps!
(see fiddle)
I would just pre-process the data. In the original root structure you know the countries are 2 levels down so:
var countries = [];
root.children.forEach(function (c){
c.children.forEach(function (d){
countries.push(d.name.replace(/[_-]/g, " "));
});
});
And the dropdown becomes:
var options = dropDown.selectAll("option")
.data(countries)
.enter()
.append("option");
options.text(function (d) {
return d;
}).attr("value", function (d) {
return d;
});
Updated fiddle.

How to initiate zoomability in a Sunburst with selection from dropdown

I have a sunburst for which I would like to initiate zoom ability via dropdown. That is when a country name is selected from the drop down menu its part in the sunburst zooms exactly like when its clicked.
js fiddle: http://jsfiddle.net/8wd2xt9n/7/
var width = 960,
height = 700,
radius = Math.min(width, height) / 2;
var b = {
w: 130, h: 30, s: 3, t: 10
};
var x = d3.scale.linear()
.range([0, 2 * Math.PI]);
var y = d3.scale.sqrt()
.range([0, radius]);
var changeArray = [100,80,60,0, -60, -80, -100];
var colorArray = ["#67000d", "#b73e43", "#d5464a", "#f26256", "#fb876e", "#fca78e", "#fcbba1", "#fee0d2", "#fff5f0"];
var svg = d3.select("#diagram").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + (height / 2 + 10) + ")");
var partition = d3.layout.partition()
.value(function(d) { return d.Total; });
var arc = d3.svg.arc()
.startAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x))); })
.endAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx))); })
.innerRadius(function(d) { return Math.max(0, y(d.y)); })
.outerRadius(function(d) { return Math.max(0, y(d.y + d.dy)); });
console.log(arc)
function checkIt(error, root){
initializeBreadcrumbTrail();
//intilaize dropdown
if (error) throw error;
var g = svg.selectAll("g")
.data(partition.nodes(root))
.enter().append("g");
var path = g.append("path")
.attr("d", arc)
.style("fill", function(d) { var color;
if(d.Total_Change > 100)
{color = colorArray[0]
}
else if(d.Total_Change > 0 && d.Total_Change < 100)
{color = colorArray[1]
}
else
{
color = colorArray[2]
}
d.color = color;
return color})
.on("click", click)
.on("mouseover", mouseover);
var tooltip = d3.select("body")
.append("div")
.attr("id", "tooltips")
.style("position", "absolute")
.style("background-color", "#fff")
.style("z-index", "10")
.style("visibility", "hidden");
g.on("mouseover", function(d){return tooltip.style("visibility", "visible")
.html("<div class=" + "tooltip_container>"+"<h4 class=" + "tooltip_heading>" +d.name.replace(/[_-]/g, " ") +"</h4>" +"<br>" + "<p> Emissions 2013:" + " " + "<br>" + d.Total + " " +"<span>in Million Tons</span></p>"+ "<br>"+ "<p> Change in Emissions: <span>" + (d.Total_Change/d.Total*100).toPrecision(3) + "%" + "</span></p>"+"</div>" );})
.on("mousemove", function(){return tooltip.style("top",(d3.event.pageY-10)+"px").style("left",(d3.event.pageX+10)+"px");})
.on("mouseout", function(){return tooltip.style("visibility", "hidden");});
//creating a dropdown
var dropDown = d3.select("#dropdown_container")
.append("select")
.attr("class", "selection")
.attr("name", "country-list");
var nodeArr = partition.nodes(root);
var options = dropDown.selectAll("option")
.data(nodeArr)
.enter()
.append("option");
options.text(function (d) {
var prefix = new Array(d.depth + 1);
var dropdownValues = d.name.replace(/[_-]/g, " ");
return dropdownValues;
}).attr("value", function (d) {
var dropdownValues = d.name;
return dropdownValues;
});
// transition on click
function click(d) {
// fade out all text elements
path.transition()
.duration(750)
.attrTween("d", arcTween(d))
.each("end", function(e, i) {
// check if the animated element's data e lies within the visible angle span given in d
if (e.x >= d.x && e.x < (d.x + d.dx)) {
// get a selection of the associated text element
var arcText = d3.select(this.parentNode).select("text");
// fade in the text element and recalculate positions
arcText.transition().duration(750)
.attr("opacity", 1)
.attr("transform", function() { return "rotate(" + computeTextRotation(e) + ")" })
.attr("x", function(d) { return y(d.y); });
}
});
};
d3.select(".selection").on("change", function changePie() {
var value = this.value;
var index = this.selectedIndex;
var dataObj = nodeArr[index];
path[0].forEach(function(p){
var data = d3.select(p).data();//get the data from the path
if (data[0].name === value){
path.transition()
.duration(750)
.attrTween("d", arcTween(d))
.each("end", function(e, i) {
// check if the animated element's data e lies within the visible angle span given in d
if (e.x >= d.x && e.x < (d.x + d.dx)) {
// get a selection of the associated text element
var arcText = d3.select(this.parentNode).select("text");
// fade in the text element and recalculate positions
arcText.transition().duration(750)
.attr("opacity", 1)
.attr("transform", function() { return "rotate(" + computeTextRotation(e) + ")" })
.attr("x", function(d) { return y(d.y); });
}
});
}
});
console.log(this.value + " :c " + dataObj["Iron and steel"] + " in " + (dataObj.parent && dataObj.parent.name));
});
};
d3.json("https://gist.githubusercontent.com/heenaI/cbbc5c5f49994f174376/raw/55c672bbca7991442f1209cfbbb6ded45d5e8c8e/data.json", checkIt);
d3.select(self.frameElement).style("height", height + "px");
// Interpolate the scales!
function arcTween(d) {
var xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]),
yd = d3.interpolate(y.domain(), [d.y, 1]),
yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius]);
return function(d, i) {
return i
? function(t) { return arc(d); }
: function(t) { x.domain(xd(t)); y.domain(yd(t)).range(yr(t)); return arc(d); };
};
}
function initializeBreadcrumbTrail() {
// Add the svg area.
var trail = d3.select("#sequence").append("svg:svg")
.attr("width", width)
.attr("height", 50)
.attr("id", "trail");
// Add the label at the end, for the percentage.
trail.append("svg:text")
.attr("id", "endlabel")
.style("fill", "#000");
}
function breadcrumbPoints(d, i) {
var points = [];
points.push("0,0");
points.push(b.w + ",0");
points.push(b.w + b.t + "," + (b.h / 2));
points.push(b.w + "," + b.h);
points.push("0," + b.h);
if (i > 0) { // Leftmost breadcrumb; don't include 6th vertex.
points.push(b.t + "," + (b.h / 2));
}
return points.join(" ");
}
// Update the breadcrumb trail to show the current sequence and percentage.
function updateBreadcrumbs(nodeArray) {
// Data join; key function combines name and depth (= position in sequence).
var g = d3.select("#trail")
.selectAll("g")
.data(nodeArray, function(d) { return d.name.replace(/[_-]/g, " ") + d.Total; });
// Add breadcrumb and label for entering nodes.
var entering = g.enter().append("svg:g");
entering.append("svg:polygon")
.attr("points", breadcrumbPoints)
.style("fill", "#d3d3d3");
entering.append("svg:text")
.attr("x", (b.w + b.t) / 2)
.attr("y", b.h / 2)
.attr("dy", "0.35em")
.attr("text-anchor", "middle")
.text(function(d) { return d.name.replace(/[_-]/g, " "); });
// Set position for entering and updating nodes.
g.attr("transform", function(d, i) {
return "translate(" + i * (b.w + b.s) + ", 0)";
});
// Remove exiting nodes.
g.exit().remove();
// Now move and update the percentage at the end.
d3.select("#trail").select("#endlabel")
.attr("x", (nodeArray.length + 0.5) * (b.w + b.s))
.attr("y", b.h / 2)
.attr("dy", "0.35em")
.attr("text-anchor", "middle");
// Make the breadcrumb trail visible, if it's hidden.
d3.select("#trail")
.style("visibility", "");
}
function getAncestors(node) {
var path = [];
var current = node;
while (current.parent) {
path.unshift(current);
current = current.parent;
}
return path;
}
function mouseover(d) {
var sequenceArray = getAncestors(d);
updateBreadcrumbs(sequenceArray);}
Add this one line and it will work :)
Inside your selection change add this var d = data[0]; as shown below:
d3.select(".selection").on("change", function changePie() {
var value = this.value;
var index = this.selectedIndex;
var dataObj = nodeArr[index];
path[0].forEach(function(p){
var data = d3.select(p).data();//get the data from the path
if (data[0].name === value){
var d = data[0];//this line is missing
path.transition()
Working code here
Other option is to call the click function on selection change like below
d3.select(".selection").on("change", function changePie() {
var value = this.value;
var index = this.selectedIndex;
var dataObj = nodeArr[index];
path[0].forEach(function (p) {
var data = d3.select(p).data(); //get the data from the path
if (data[0].name === value) {
console.log(data)
click(data[0]);//call the click function
}
Working code here
Hope this helps!

Categories