jsfiddle https://jsfiddle.net/z7ju3z1q/3/
The problem is, I can't make titles stick with groups with working simulation.
Changing line 76 from simulation.nodes(nodes); to simulation.nodes(node); breaks simulation (apparently), but makes title stick to circles. As I understand, the problem is that dragging working with circles but not with groups, for some reason. And that's is the problem Iwe been facing whole day.
I tried describing title like this (line 38)
var title = g.selectAll("text");
And then adding it to tick (line 84)
function ticked() {
title.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
}
As I understand, the problem is that dragging working with circles but not with groups, for some reason
You are manipulating the nodes on drag with cx and cy properties. g elements do not have these, so this will not achieve what you want, even if node contained groups rather than circles:
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
As noted,node = g.append("circle") means that you aren't actually manipulating the g elements anyways, which is why your circles move on tick or drag.
Instead, keep node a selection of g elements, and manipulate the transform property:
// the group representing each node:
node = node.enter().append("g").merge(node);
// the circle for each group
node.append("circle")
.classed('node', true).attr('id', id)
.text(id)
.attr("r", 25).attr("fill", function(d) { return color(d.id); });
// the text for each group
node.append("text")
.classed('text', true)
.attr("text-anchor", "start")
.attr("dx", 6).text(id).merge(node);
Then, on click or drag events, just update the transform:
Tick:
node.attr("transform",function(d) { return "translate("+d.x+","+d.y+")" ;});
Drag:
d.x = d3.event.x;
d.y = d3.event.y;
d3.select(this).attr("transform",function(d) { return "translate("+d.x+","+d.y+")" ;});
Here's an updated fiddle.
Related
I am trying to create a force graph as shown in below image. I am checking the graph given at this link force tree. This tree is vertical. I want it horizontal so that I can achieve the graph shown in the image. I tried to play with the code but was not able to make it horizontal. Is there any way I can achieve the graph shown in the image.
desired force graph
Turning the tree horizontal is actually quite simple in the given example.
The tick function is responsible for assigning positions to each node, and it modifies the y value of the nodes between levels. Simply change it to modify the x value of the nodes.
Like so:
function tick(e) {
var k = 6 * e.alpha;
// Push sources up and targets down to form a weak tree.
link
// Swapped here from y to x
.each(function(d) { d.source.x -= k, d.target.x += k; })
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
Using D3 ver 3.5.5. I am using an example (https://gist.github.com/stepheneb/1182434) as a template: the example code to draw the data looks like this:
var circle = this.vis.select("svg").selectAll("circle")
.data(this.points, function(d) { return d; });
circle.enter().append("circle")
.attr("class", function(d) { return d === self.selected ? "selected" : null; })
.attr("cx", function(d) { return self.x(d.x); })
.attr("cy", function(d) { return self.y(d.y); })
.attr("r", 10.0)
.style("cursor", "ns-resize")
.on("mousedown.drag", self.datapoint_drag())
.on("touchstart.drag", self.datapoint_drag());
circle
.attr("class", function(d) { return d === self.selected ? "selected" : null; })
.attr("cx", function(d) {
return self.x(d.x); })
.attr("cy", function(d) { return self.y(d.y); });
circle.exit().remove();
I think of this as four sections: the first does selectAll("circles") and adds the data. The second tells where the data points are ("cx", "cy") and other attr(), and the third is a bit of mystery to me, because it appears to also set "cx" and "cy", but no other attributes. Finally, we do and exit().remove(), which the documentation says removes any data elements not associated with the data array. I dont see how this is happening in this example. When I set breakpoints into the code, both the "cx" steps get called for each data point in the this.points array.
In my code, I try to do the same steps:
hr_circles = self.graph_gps.svg.selectAll("hr_circles")
.data(self.graph_gps.datay1); // , function(d){return d;}
hr_circles.enter().append("circle")
.style("z-index", 3)
.attr("class", "y1")
.attr("r", 1)
.attr("cx", function (d, i) {
return xScale(d.time)
})
.attr("cy", function (d, i) {
return yScale(d.vy)
})
.on("mouseover",
function (d) {...displays a tooltip...})
.on("mouseout", function (d) {
});
hr_circles.attr("class", "y1")
.attr("cx", function (d, i) {
return xScale(d.time)
})
.attr("cy", function (d, i) {
return yScale(d.vy)
})
hr_circles.exit().remove();
When my graph initially displays, the data appear just fine, properly scaled, etc. When I try to re-scale by dragging on the x-axis (as in the example), the axis rescales itself just fine, and re-scaled data appears on the graph, but the original data is also still there (no longer scaled correctly), making a big mess! How do you erase or make the originally scaled data go away?
Tried to post images, but I guess my reputation is too low. Will send to anyone interested.
I have this visualization and I'm trying to add fisheye view to the chart. I have tried adding it with the following lines in the plotData function but it doesn't happen:
var fisheye = d3.fisheye.circular()
.radius(120);
svg.on("mousemove", function () {
fisheye.focus(d3.mouse(this));
circle.each(function (d) {
d.fisheye = fisheye(d);
});
});
Any ideas on how to solve this?
Thanks!
First things first, your d3.timer never stops running. This is driving my machine crazy (cpu 100%) and killing the performance of the fishey. I'm really not sure what you are doing there, so ignoring that for a moment.
Your fisheye needs a little massaging. First, it expects your data pixel's positions to be stored in d.x and d.y attributes. You can fudge this in with when drawing your circles:
circle
.attr("cx", function(d, i) { d.x = X(d[0]); return d.x; })
.attr("cy", function(d, i){ d.y = Y(d[1]); return d.y; });
Second, you are plotting your data in multiple steps, so you need to select all the circles for the fisheye. And third, you forgot the code that actually makes the points grow and shrink:
svg.on("mousemove", function () {
fisheye.focus(d3.mouse(this));
// select all the circles
d3.selectAll("circle.data").each(function(d) { d.fisheye = fisheye(d); })
// make them grow and shrink and dance
.attr("cx", function(d) { return d.fisheye.x; })
.attr("cy", function(d) { return d.fisheye.y; })
.attr("r", function(d) { return d.fisheye.z * 4.5; });
});
Updated example.
I would like to reproduce the process from D3 Sankey chart using circle node instead of rectangle node, however, I would like to select only certain nodes to change from rectangles to circles.
For example, in this jsfiddle used in the example, how would you only select Node 4 and Node 7 to be converted to a circle?
I updated your fiddle.
Basically you just need some way to select the nodes that you want to make different. I used unique classname so that you can style them with CSS as well. I didn't feel like writing the code to select just 4 and 7 (I'm lazy) so I just selected all of the even nodes instead.
// add in the nodes
var node = svg.append("g").selectAll(".node")
.data(graph.nodes)
.enter().append("g")
.attr("class", function (d, i) { return i % 2 ? "node rect" : "node circle";
})
Then you can use that to select the nodes and add circles instead of rectangles.
svg.selectAll(".node.circle").append("circle")
.attr("r", sankey.nodeWidth() / 2)
.attr("cy", function(d) { return d.dy/2; })
.attr("cx", sankey.nodeWidth() / 2)
.style("fill", function (d) {
There is also another similar approach, illustrated in the following jsfiddle.
I started from this fiddle (from another SO question that you merntioned)), where all nodes had already been converted to circles:
Then I modified existing and added some new code that involves filtering during creation of circles:
// add the circles for "node4" and "node7"
node
.filter(function(d){ return (d.name == "node4") || (d.name == "node7"); })
.append("circle")
.attr("cx", sankey.nodeWidth()/2)
.attr("cy", function (d) {
return d.dy/2;
})
.attr("r", function (d) {
return Math.sqrt(d.dy);
})
.style("fill", function (d) {
return d.color = color(d.name.replace(/ .*/, ""));
})
.style("fill-opacity", ".9")
.style("shape-rendering", "crispEdges")
.style("stroke", function (d) {
return d3.rgb(d.color).darker(2);
})
.append("title")
.text(function (d) {
return d.name + "\n" + format(d.value);
});
// add the rectangles for the rest of the nodes
node
.filter(function(d){ return !((d.name == "node4") || (d.name == "node7")); })
.append("rect")
.attr("y", function (d) {
return d.dy/2 - Math.sqrt(d.dy)/2;
})
.attr("height", function (d) {
return Math.sqrt(d.dy);
})
.attr("width", sankey.nodeWidth())
.style("fill", function (d) {
return d.color = color(d.name.replace(/ .*/, ""));
})
.style("fill-opacity", ".9")
.style("shape-rendering", "crispEdges")
.style("stroke", function (d) {
return d3.rgb(d.color).darker(2);
})
.append("title")
.text(function (d) {
return d.name + "\n" + format(d.value);
});
Similar code had to be modified for accurate positioning text beside rectangles.
I believe the final result looks natural, even though it lost some of the qualities of the original Sankey (like wider connections).
I'm trying to add text to nodes that are created dynamically, specifically, like this graph:
http://bl.ocks.org/mbostock/999346
I am NOT looking to add text to collapsible graphs, but to add text to graphs that insert nodes at runtime, as shown in the above example.
So far I have the following additional code:
node.enter().append("text", "g")
.attr("x", function(d) { return (d.x);})
.attr("y", function(d) { return (d.y);})
.text(function(d) { return d.name; })
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; });
This adds text but it doesn't move when nodes are inserted.
The below lines appear to move the nodes and links in the example:
var t = svg.transition()
.duration(duration);
t.selectAll(".link")
.attr("d", diagonal);
t.selectAll(".node")
.attr("cx", function(d) { return d.px = d.x; })
.attr("cy", function(d) { return d.py = d.y; });
but adding a similar function for text:
t.selectAll(".text")
.style("fill-opacity", 1);
has no effect.
I would very much appreciate any guidance on this matter.
The nodes are all identified with d3.selectAll('.node'), so you need to give them the node class.
Also, the cx and cy attributes don't exist on text elements. you want x and y instead.
Example fiddle is here.