Does d3.js support dynamic Sunbursts - javascript

I would like to create a mashup of the functionalities as seen from
http://bl.ocks.org/4063423 and http://philogb.github.com/jit/static/v20/Jit/Examples/Sunburst/example2.html
I would like to use d3.js or at least a pure javascript solution but a solution that will respond to mouse clicks to display more information about the selected section.
Zooming in and out is not mandatory, but if I can achieve it, it will be good.
Now my question, Is there a framework that can support this or do I have to mash them up on my own.
Disclaimer: google was not that helpful!

It is easy to do with D3 alone: http://bl.ocks.org/4678148 If you click any element, the element will be focused and transitioned to 90 deg with the selected class set on it.
Also, the legend text on the top right changes to the name of the element selected. The part of code which achieves this coupling is:
d3.selectAll("path").on("click", function (d, i) {
var newAngle = - (d.x + d.dx / 2);
innerG
.transition()
.duration(1500)
.attr("transform", "rotate(" + (180 / Math.PI * newAngle) + ")");
// Set the class "selected" on the chosen element.
path
.classed("selected", function (x) { return d.name == x.name; });
// Update the text box with the right context
// This can be arbitrarily complex to show as many details about the
// object as required.
textBox.data(["Clicked: " + d.name])
.text(String);
});
Update
For the zoomable behavior such that the clicked element transitions to the center, you can use almost the same code as used as here or here. I have made small changes to the code to show how to extract information about which item was clicked: http://bl.ocks.org/4747342
The change in code required is simpler than before:
d3.selectAll("path").on("click", function (d, i) {
// Zooming
path.transition()
.duration(750)
.attrTween("d", arcTween(d));
// Update the text box with the right context
// This can be arbitrarily complex to show as many details about the
// object as required.
textBox.data(["Clicked: " + d.name])
.text(String);
});

Related

Iterating throug SVG elements with D3.js

What I'm trying to do is relatively simple but I'm new to JS and D3.js.
I have created a bunch of rectangles using SVG through D3.js.
I added some code to handle a click event and in there I'd like to iterate through all drawn nodes and do something with them as long as a specific property matches the same property in the one that's been clicked.
Here's the code that draws the rectangles (only one of them here);
d3.select("svg")
.append("rect").attr("x", 50)
.attr("y", 10)
.attr("height", 20)
.attr("width", 200)
.attr("title", "catalog")
.style("fill", "#CB4B19")
.on("click", mouseClick)
And here's how I'm trying to retrieve the "title" property of each rectangle drawn and compare it to the clicked one (and in this case, just log it in the console). I know this is probably basic but I can't figure out what I'm doing wrong here.
function mouseClick(d) {
var t = d3.select(this).attr("title"); //store the "title" property of the clicked rectangle
d3.selectAll("rect").each(function(d, i){ //Select all rectangles drawn
if(d3.select(this).attr("title") == t){ //for each one, if the "title" property = the one initially chosen
console.log(t); //do something here
}
})
}
Your code actually seems to be working correctly. At least for me it did. One thing I will say is that d3 does mimic jQuery syntax in that it lets you select elements with attributes with the d3.select('element[attributeName="?"]') syntax. You can read more about selections here.
So for your example, you could do
var t = d3.select(this).attr("title");
// select all rectangles with the attribute title
d3.selectAll("rect[title='" + t + "']").each(function(d, i){
console.log(t);
});
You no longer need the if statement to check because you are only selecting them. I made a simple jsFiddle to show this. I made 3 different types of rectangles with different title attributes and when you click on them, it only selects rect that have the same title attribute.
http://jsfiddle.net/augburto/znqe8nqr/

Avoid dx/dy attributes in d3 axis labels

I experienced problems with the position of text elements when exporting SVG files and opening it in Corel Draw (some older version). I fixed it by setting every dx/dy attribute to zero and added its value to the corresponding x/y attribute.
I wrote a helper function which is called with .each on every text element I use.
transformDXYtoXY: function(d, i) {
var that = d3.select(this);
var y = that.attr("y") == null ? 0 : parseFloat(that.attr("y"));
var dy = that.attr("dy") == null ? 0 : parseFloat(that.attr("dy"));
that.attr("y", y + dy);
that.attr("dy", 0);
// doing the same with dx/x
...
},
This was working great until I decided to transition axis on input change instead of redraw them:
axis = d3.svg.axis().scale(someScale);
d3.select('.axis')
.transition()
.call(axis)
.selectAll("text")
.each(transformDXYtoXY);
Without the call to transformDXYtoXY() the tick label position is off
The y/dy attributes are not being set, even though when I check for it inside transformDXYtoXY() it seems allright.
Is there a way to tell d3 to avoid using dx/dy? It looks like the problem occurs during transition().
The use of the dx and dy attributes is hardcoded in the source of D3 -- changing it would be a significant effort. However, there's an easy workaround. D3 transitions allow you to set up a listener for the end of the transition. You can leverage this to run your code to fix the attribute values (with minimal changes to your existing code):
d3.select('.axis')
.transition()
.call(axis)
.selectAll("text")
.each("end", transformDXYtoXY);
To clarify, the code that you have at the moment runs the function to fix the attributes immediately after setting up the transition which then runs and overwrites the attribute values. The code above runs the function after the transition is complete, i.e. no further attribute changes will occur.

Zooming issues with Force Directed

I've generated a D3 visualization (a force directed graph) that requires zooming and panning. I've got 2 problems however when it comes to zooming, and I can't find any decent examples on how I might overcome these problems:
The first problem is I've followed all the examples I can find about zooming, which involves adding groupings and adding a rectangle to ensure that the entire area is zoomeable. If I style the rectangle a slightly opaque blue then I get SVG that looks like this when I zoom out:
The problem with this is that I can zoom in/out absolutely fine while I've got my mouse over the blue rectangle area. The problem is I want this to be fully opaque, which means that when I zoom right out, it's very easy to place the cursor outside of this box and then you're unable to zoom in. Is there a way I can make the SVG itself zoomeable or pick up on these events?
This is how I go about generating the various layers and the zoomed function:
function zoomed() {
group2.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
svg = d3.select(target)
.attr("pointer-events", "all")
.append("svg");
group = svg.append('svg:g')
.call(d3.behavior.zoom().on('zoom', zoomed))
.on("dblclick.zoom", null);
group2 = group.append("g");
rect = group2.append('svg:rect')
.style("opacity", 0.3)
.attr('width', width)
.attr('height', height);
The second problem I have is that I'm trying to automatically size my text based on this http://bl.ocks.org/mbostock/1846692 example. When I've tried this however I seem to be getting text that renders really poorly. It seems to suffer from:
Being difficult to read
Not appearing contained within the circle
Being so small the entire thing compresses (image 2)
var texts = planets.append("text")
.text(function(d) { return d.name; })
.style("font-size", "24px") // initial guess
.style("font-size", function(d) {
return Math.min( 2 * d.size, (2 * d.size - 8) / this.getComputedTextLength() * 24) + "px";
})
.attr("dx", function(d) { return -d.size; })
.attr("dy", ".35em")
.style("fill", "white");
I thought that SVG would just handle this, I understand that some of the font-sizes can come out small, but if you zoom in should that not all sort itself out?
I've got a JSFiddle http://jsfiddle.net/IPWright83/vo7Lpefs/22/ to demonstrate.
I've not yet managed to work out a resolution to my first issue (regarding the zooming box) however I did manage to track down the text rendering issue.
This was actually because the each circle/node had a stroke property to provide the white border. This was also applying to the text element, and when the font was very small the stroke was much larger than the overall fill of the text. Removing the stroke from the text elements ensured that they rendered even when very small.

Selections in custom plugins in mpld3 -- Making a slider

I'm trying to implement a slider in mpld3, much like this previous question.
I'm trying to build off of the draggable points example to do this. I'm having a bit of trouble understanding how the following bit of code works:
function dragged(d, i) {
d[0] = obj.ax.x.invert(d3.event.x);
d[1] = obj.ax.y.invert(d3.event.y);
d3.select(this)
.attr("transform", "translate(" + [d3.event.x,d3.event.y] + ")");
}
In particular, what does this refer to in this context. I originally thought that I could replace d3.select(this) with something like d3.select("#"+foo) where foo = this.props.id (at the top of the draw() function). But this doesn't work, as shown in this notebook I made. (The second piece of code doesn't allow you to drag the red dots around).
In case what I'm trying to do isn't clear... have a look at this notebook. I've made a plugin that allows the red square (the slider) to be dragged horizontally. What I would like to do is make dragging the red dot change the position of the blue dot. So I want to do something like:
function dragged(d, i) {
d[0] = obj.ax.x.invert(d3.event.x);
sliderPosition = obj.ax.x(d[0]);
targetPosition = obj.ax.x(-d[0]); // inverted sign
d3.select("#redsquare")
.attr("transform", "translate(" + [sliderPosition,sliderObj.ax.y(d[1])] + ")");
d3.select("#bluedot")
.attr("transform", "translate(" + [targetPosition,targetObj.ax.y(d[1])] + ")");
}
The intended behavior for this simple example is to have the blue dot move in the opposite direction of the red square when it is dragged. The question is, what do I put in place of "#redsquare" and "#bluedot"?
Many thanks.
I know a hacky way to do this. Instead of d3.select(this), you can find the specific element you are interested in the obj element array as follows:
d3.select(obj.elements()[0][i])
There must be a prettier way, though.

d3 - hover on legend to highlight the respective data

Successfully created a heatmap using d3.
Here's the FIDDLE.
I have some basic idea on using d3's mouseover events. But now I wanted move a step ahead.
This is what I'm looking for. When I hover on a legend, I wanted the hovered legend's respective data to be highlighted in the chart.
Can someone help me to achieve it?
You're not binding the data to the legend, which makes this task a bit more difficult, but you can still do it fairly easily. The idea is to assign a class defined by the fill color to the rect elements and then select accordingly in the mouseover handler. The code looks like this.
// for the rectangles
.attr("class", function(d) {
return "hour bordered " + "color-" + colorScale(d.value).substring(1);
})
// for the legend
.on("mouseover", function(d, i) {
svg.selectAll("rect.color-" + colors[i].substring(1)).style("stroke", "blue");
})
.on("mouseout", function(d, i) {
svg.selectAll("rect.color-" + colors[i].substring(1)).style("stroke", "white");
});
Complete example here.

Categories

Resources