d3 force directed graph nodes stay at fixed position after filter - d3.js

In my d3 force directed scatter plot i try to make points disappear and re-appear by clicking on a legend key. After clicking the legend key, i would like the remaining points to regroup and not to stay fixed in the same position, leaving blank spaces (screenshots). When clicking again on the legend, they should fly in again.
I tried to remove the fill of the circles upon clicking on a legend key, which is working, but obviouly does not make the force do its work..
My code on blockbuilder.org: http://blockbuilder.org/dwoltjer/04a84646720e1f82c16536d5ef9848e8

You can treat the filtered data as new data and apply the update, enter and exit pattern:
var node = svg.selectAll(".dot")
.data(data);
node.exit().remove();
node.enter().append("circle")
.attr("class", "dot")
.attr("r", radius)
......
The click event for legend:
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color)
.on("click", function (d) {
visible[d] = !visible[d];
var newdata = data.filter(function(e) { return visible[e.bank];});
DrawNode(newdata);
});
Here is the update blocks

Simply deleting the nodes should be enough to make the force rearrange itself and group the nodes again. But, you will want to save the nodes to bring them back (possibly using a temporary array).
However, if you want the nodes to fly off screen (and back on), then what I'd do (using V4) is move the nodes to a new forcePoint that's way off screen:
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color)
.on("click", function (d) {
node.filter(function () {
return this.dataset.bank === d;
})
position
.force('x', d3.forceX(width/2).strength(20))
.force('y', d3.forceY(height*2).strength(20));//should be twice the height of the svg, so way off the y axis. Or whichever direction you choose.
});

Related

d3js scatterplot custom icons and animation

I'm just starting with d3js and I wanted to know if it's possible to create a scatterplot with custom icons for the data points similar to this method for Forced Layout?
I don't want to use d3.svg.symbol() as I want to use a custom icon with my company logo to mark each data point.
My final goal is to translate a point (with a custom icon) along a horizontal axis depending on the x value. I've searched high and low on how to do this with d3js but have had no luck.
To use an icon instead of a symbol, just swap out the path element (that the symbol generator is called on) with an image element.
Given a data set, D, with elements like {src: http.myImageURL, x: 10, y : 20} it would look something like this:
var svg = d3.select('body').append('svg');
svg.append('g').selectAll('.myPoint')
.data(D)
.enter()
.append('image')
.attr("xlink:href", function(d){ return d.src })
.attr("x", function(d){ return d.x })
.attr("y", function(d){ return d.y })
.attr("width", 16)
.attr("height", 16);

Add custom html to nodes in d3 js tree

I'm trying to build a tree like d3 js tree. I need to add a div and 2 or 3 buttons in that div for each node of the tree. Clicking on that node button should show a popup.
I'm trying for this kind of functionality
There are other plugins similar to this. But i need this in d3 js tree as its navigation and animations are smooth.
I have done this:
Use base example from D3 tree web page.
Added more SVG elements in the nodes
Added a "popup" menu when you click the node text (Add, Remove, Edit, Move) to perform this simple operations on the node.
In my experience, it is better to use SVG elements instead of a DIV (You can display buttons as images or shapes, and text as svg:text.
Here is some code:
function clickLabel(d) {
// this removes the popup if it was displayed on another node beforehand
// is=2 identifies markers...
d3.selectAll("[marker='2']").remove();
// Every node has an ID, and I add shapes to it
d3.select("[id_node='" + d.id + "']")
.append("image")
.attr("marker", 2)
.attr("xlink:href", "/Content/delete_item.png")
.attr("x", 0)
.attr("y", -50)
.attr("height", 32)
.attr("width", 32)
.on("click", removeItem);
d3.select("[id_node='" + d.id + "']")
.append("image")
.attr("marker", 2)
.attr("xlink:href", "/Content/edit.png")
.attr("x", -50)
.attr("y", -30)
.attr("height", 32)
.attr("width", 32)
.on("click", editItem);
d3.select("[id_node='" + d.id + "']")
.append("image")
.attr("marker", 2)
.attr("xlink:href", "/Content/add_item.png")
.attr("x", 20)
.attr("y", 10)
.attr("height", 32)
.attr("width", 32)
.on("click", addItem);
d3.select("[id_node='" + d.id + "']")
.append("image")
.attr("marker", 2)
.attr("xlink:href", "/Content/next_item.png")
.attr("x", -30)
.attr("y", 20)
.attr("height", 32)
.attr("width", 32)
.on("click", moveItem);
// Stop events or else it gets de-selected
event.stopPropagation();
}
Hope this helps!
You can use .append("svg:foreignObject") to add custom html to nodes in d3 js tree, for example jsfiddle example

How to make d3 area transition originate from bottom

How can I make the transition of an area originate at the bottom of an svg rather than the top? When changing the height using this area function it will originate at the top.
area: function(width, height) {
var x = this.xScale(width),
y = this.yScale(height);
return d3.svg.area()
.x(function(d) { return x(d.x); })
.y0(height)
.y1(function(d) { return y(d.y); });
}
Transition with new height h:
var area = this.area(w, h);
svg.datum(data)
.transition()
.ease('linear', 1, .3)
.duration(1000)
.attr('d', area);
I've found this SO question but can't translate it to my problem:
D3.js Transitions
Update
Here is the code: http://jsfiddle.net/g3yS5/12/
The area a1 is situated at the top and when transitioning to a2 it pushes from top to bottom. I guess the solution to the problem involves getting the area to render to the bottom initially? If so how could I do this?
Is this what you want?
svg.select('path')
.datum(data)
.attr('transform', 'translate(0, 80)') // <---- here
.transition()
.ease('linear', 1, .3)
.duration(1000)
.attr('transform', 'translate(0, 0)') // <---- then here
.attr('d', a2);
It first translates the path to be at the bottom of the new height, then transitions that back to its natural position (NOTE: the translation is hard coded to be 80; you would want to compute it based on the height delta). This results in the path jumping to the new position, which might be what you expect.
Otherwise, you can also transition it in two steps. See this jsFiddle.
Transitions start from whatever value the attribute you're animating currently is, so you can simply change that value before the transition starts:
svg.datum(data)
.attr('d', startingArea) // set initial 'state'
.transition().ease('linear', 1, .3).duration(1000)
.attr('d', area);
In this case, startingArea could look a lot like your existing area function, but y0 would be 0 perhaps, or maybe y1 would be height. I'd have to see your code for more a more specific solution.

D3: How to access an attribute of a previous item

I am using D3 to plot a rectangle for each object in an array, the height of the rectangle being dependant on the 'Size' property of the object. These rectangles are stacked on top of each other. I currently set the y position by summing the 'Size' of each subsequent rect that gets plotted - but this seems wrong - and I was wondering if there was a better way to do this, such as accessing the 'y' attribute of the previous item (and how?) or another way...
This is what the essence of my code looks like. There is a link to the fiddle below.
var cumY = 0;
var blocks1 = sampleSVG.selectAll("rect")
.data(fpp)
.enter()
.append("rect")
.sort(SortBySize)
.style("stroke", "gray")
.style("opacity", blockOpacity)
.style("fill", function (d) {return d.Colour})
.attr("width", 80)
.attr("height", function (d) {return d.Size})
.attr("x", 5)
.attr("y", function (d, i) {
var thisY = cumY;
cumY += d.Size;
// perhaps I could just return something like d.Size + previousItem.GetAttribute("y") ???
return thisY;
});
http://jsfiddle.net/ninjaPixel/bvER3/
This is tricky do! You're right that keeping track of the cumulative height 'seems wrong' - it works now but it isn't very idiomatic d3 and will get pretty messy once you start trying to do something more complicated.
I would try using d3's built in stack-layout which was created solve this problem. You might want to start working off of this example and posting an updated fiddle if you get stuck. Good luck!

D3.js ignores duplicate values

I'm exploring D3.js. I primarily used their tutorial to get what I have. But I have made some adjustments.
What I'm doing is counting the number of active & inactive items in a specific table. It then displays a graph with those values. Most everything works fines. I have 2 issues with it though:
It doesn't update automatically with my AJAX call when items are deleted. Only updates when items are added. But I'm not concerned about this for this post.
My primary issue: duplicate values aren't being treated as individual numbers. Instead it sees [10,10] and outputs it as a single bar in the graph as 10 (instead of 2 bars).
Looking at the D3 docs, it would seem the issue lies with .data. Documentation mentions that it joins data.
$(document).on("DOMSubtreeModified DOMNodeRemoved", ".newsfeed", function() {
var columns = ['created','deleted'];
var data = [numN, numD];
var y = d3.scale.linear()
.domain([0, d3.max(data)])
.range([0, 420]);
var x = d3.scale.ordinal()
.domain(columns)
.rangeBands([0, 120]);
chart.selectAll("rect")
.data(data)
//.enter().append("rect")
.attr("x", x)
.attr("height", y)
.attr("width", x.rangeBand())
.attr("rx", 10)
.attr("ry", 10);
chart.selectAll("text")
.data(data)
//.enter().append("text")
.attr("y", y)
.attr("x", function(d) {
return x(d) + x.rangeBand() / 2;
}) //offset
.attr("dy", -10) // padding-right
.attr("dx", ".35em") // vertical-align: middle
.attr("text-anchor", "end") // text-align: right
.text(String);
});
How can I make each value to display? If I pass in two different values, the chart displays as it should.
Your problem is at .attr("x", x). So the way you're doing it assigns the same x coordinate for both rects.
So try offsetting x coordinate.
.attr("x", function(d, i) { x + i * width_of_your_rect); })

Resources