Detecting visible nodes in d3.js force layout after zoom-pan - svg

I use a force directed layout in d3.js once on load to position nodes and edges. Then I can zoom and pan the svg. When I zoom in I want to detect which nodes and edges are visible so I can implement lazy loading of additional data only for the visible nodes and edges.
Does anyone know what is the best way to get the (partialy) visible elements?
Code is below (just pasted some examples together):
var svg = d3.select("#chart")
.append("svg")
.attr("width", width)
.attr("height", height)
.attr("pointer-events", "all")
.append('svg:g')
.call(d3.behavior.zoom().on("zoom", redraw))
.append('svg:g')
svg.append('svg:rect')
.attr('width', width)
.attr('height', height)
.attr('fill', 'white')
function redraw() {
trans=d3.event.translate;
scale=d3.event.scale
svg.attr("transform", "translate(" + trans + ")" + " scale(" + scale + ")")
console.log(scale)
}

If you are using scales, you can find the currently visible range using scale.invert. So to find the visible values for an axis where the width is 600px, use x.invert(0) and x.invert(600).

Related

Vertical arc using D3.js

How do I draw semi-circular arc along the y-axis ( perpendicular axis ) of an svg using D3 ?
Here is an image of what I want to achieve target
Here is my code so far :-
var canvas = d3.select("body").append("svg")
.attr("width",width)
.attr("height",height);
var group = canvas.append("g")
.attr("transform","translate(0,200)");
var origin = d3.svg.arc()
.outerRadius(50)
.innerRadius(0)
.startAngle(-1.5755)
.endAngle(1.5755);
var arcs1 = group.append('g')
.attr('class','arc')
.attr("transform","translate(50,0)");
var yAxis = group.append("line")
.attr("transform","translate(50,0)")
.attr("x1", 0)
.attr("y1", -200)
.attr("x2", 0)
.attr("y2", height)
.attr("stroke-width", 2)
.attr("stroke", "black");
First, to draw the arc like your target you want your startAngle to start at 0 and endAngle to be 3.14 or Math.PI.
var origin = d3.svg.arc()
.outerRadius(50)
.innerRadius(0)
.startAngle(0)
.endAngle(Math.PI);
Then just append a path to arcs1 and use your origin function to draw it.
arcs1.append("path").attr("d", origin);
You can take a look at a live demo at http://jsfiddle.net/7EfS5/.
Edit
To change the color of your arc, you can either use .attr("fill", color) or .style("fill", color)
arcs1.append("path").attr("d", origin)
.attr("fill", "red");
Hope this help.
When you make a d3 arc, an angle of 0 represents the top of a circle. So to make an arc like the one shown in your image, you will want your start angle to be 0 and your end angle to be halfway around a circle, which is Pi radians:
var arc = d3.svg.arc()
.outerRadius(50)
.innerRadius(0)
.startAngle(0)
.endAngle(Math.PI);
As far as positioning your arc, you can use an svg transform to translate the origin of the arc to the position you want. For instance, if your y-axis has height h you can translate the origin to the center of the axis when you create the path element for your arc like this:
svg.append('path')
.attr('transform', 'translate(0,' + h/2 + ')')
.attr('d', arc);
HERE is a demo.

.style of child nodes in d3js

I'm trying to make a simple graph with nodes and links. I have "g" elements containing a circle and its text, and links on their own. I have, for example, this bit on code called on a mouseover event:
//check if circle is connected to current "viewed" (mouseover-ed)
//circle via a link referenced by "that" and paint it green if so
circles.filter(function() {
return d3.select(this).attr("index") == d3.select(that).attr("src");
}).attr("viewed",1).style("stroke", "green");
});
This was really a long shot as nodes is the 'g' element container and I wasn't sure what calling .style would do, but to my surprise it did change the color - but only for the text!
Is there a way to make it change the stroke style of the circle as well?
The declaration code:
var circles = svg.append("g")
.attr("class","nodes")
.selectAll("circle")
.data(graph.nodes)
.enter()
.append("g")
.attr("transform",function(d,i){d.x = getX(i);d.y=getY(i);return "translate(" + d.x + "," + d.y + ")";})
.attr("name", function(d){return d.name;})
.attr("viewed", 0)
.attr("focused", 0)
.attr("index", function(d, i) {return i;});
circles.append("circle")
.style("stroke", "gray")
.style("fill", "white")
.attr("r", node_radius_wo_pad)
.on("mouseover", function(){...};
circles.append("text")
.attr("text-anchor","middle")
.text(function(d){return d.name});
The reason this is working is that you haven't explicitly declared a stroke colour for the text and so it inherits what you set for the parent g element. To make this work for the circles, you have to select them explicitly:
var toChange = circles.filter(function() {
return d3.select(this).attr("index") == d3.select(that).attr("src");
});
toChange.attr("viewed", 1);
toChange.selectAll("circle").style("stroke", "green");
toChange.selectAll("text").style("stroke", "green");

What is the difference between svg's x and dx attribute?

What is the difference between svg's x and dx attribute (or y and dy)? When would be a proper time to use the axis shift attribute (dx) versus the location attribute (x)?
For example, I have noticed a lot of d3 examples doing something like this
chart.append("text")
.attr("x", 0)
.attr("y", 0)
.attr("dy", -3)
.text("I am a label")
What is the advantage or reasoning for setting both y and dy when the following seems to do the same thing?
chart.append("text")
.attr("x", 0)
.attr("y", -3)
.text("I am a label")
x and y are absolute coordinates and dx and dy are relative coordinates (relative to the specified x and y).
In my experience, it is not common to use dx and dy on <text> elements (although it might be useful for coding convenience if you, for example, have some code for positioning text and then separate code for adjusting it).
dx and dy are mostly useful when using <tspan> elements nested inside a <text> element to establish fancier multi-line text layouts.
For more details you can check out the Text section of the SVG spec.
To add to Scott's answer, dy used with em (font size units) is very useful for vertically aligning text relative to the absolute y coordinate. This is covered in the MDN dy text element example.
Using dy=0.35em can help vertically centre text regardless of font size. It also helps if you want to rotate the centre of your text around a point described by your absolute coordinates.
<style>
text { fill: black; text-anchor: middle; }
line { stroke-width: 1; stroke: lightgray; }
</style>
<script>
dataset = d3.range(50,500,50);
svg = d3.select("body").append("svg");
svg.attr('width',500).attr('height', 500);
svg.append("line").attr('x1', 0).attr('x2', 500).attr('y1', 100).attr('y2', 100);
svg.append("line").attr('x1', 0).attr('x2', 500).attr('y1', 200).attr('y2', 200);
group = svg.selectAll("g")
.data(dataset)
.enter()
.append("g");
// Without the dy=0.35em offset
group.append("text")
.text("My text")
.attr("x",function (d) {return d;})
.attr("y",100)
.attr("transform", function(d, i) {return "rotate("+45*i+","+d+",100)";});
// With the dy=0.35em offset
group.append("text")
.text("My text")
.attr("x",function (d) {return d;})
.attr("y",200)
.attr("dy","0.35em")
.attr("transform", function(d, i) {return "rotate("+45*i+","+d+",200)";});
<script>
View it in Codepen
If you don't include "dy=0.35em", the words rotate around the bottom of the text and after 180 align below where they were before rotation. Including "dy=0.35em" rotates them around the centre of the text.
Note that dy can't be set using CSS.

Vertical scroll bar in d3

I'm trying to make a page using d3 such that the top half of the page can overfill and might need a scroll bar, but the bottom half does not. Would I need two separate svg elements to accomplish this? If so do I need to use css properties to get the scrollbar to appear. I've tried looking up similar questions on stackoverflow but they require the use of jQuery which I'm trying to avoid if possible.
So here's an example of what I'm trying to do:
http://jsfiddle.net/agANT/3/
In this I draw the green rectangle with a height 300px so it extends off the first svg, but no scroll bar appears.
var svg = d3.select("body")
.append("svg")
.attr("width", 400)
.attr("height", 200);
var svg2 = d3.select("body")
.append("svg")
.attr("width", 400)
.attr("height", 200);
svg.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 400)
.attr("height", 300)
.attr("fill", "green");
svg2.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 400)
.attr("height", 200)
.attr("fill", "gray");
Thanks!
You would need two separate SVGs to accomplish this. You don't need to use any CSS, all you should need to do is set the width of the top SVG appropriately.

Putting an arrow (marker) at specific point on a path when using the d3 javascript library

I am working currently on a graph visualization and I use SVG and the D3 library. I was asked by our designer if I can put the arrowheads of the edges of the graph on a position corresponding to 80% of length of the lines.
I was able to achieve the first part - getting the position - by using the getPointAtLength method.
var svg = d3.select("body").append("svg")
.attr("width", 960)
.attr("height", 500)
var path = svg.append("path")
.attr("d", "M20,20C400,20,20,400,400,400")
.attr("fill", "none")
.attr("stroke", "black");
var pathEl = path.node();
var pathLength = pathEl.getTotalLength();
var pathPoint = pathEl.getPointAtLength(pathLength*0.5);
var point = svg.append("svg:circle")
.style("fill", "red")
.attr("r", 5)
.attr("cx", pathPoint.x)
.attr("cy", pathPoint.y);
Here is a jsfidle example
Now I wonder how ca I attach an arrowhead to this position with corresponding orientation. More important how can I do this so I can update the edges of the graph when moving the associated nodes.
I was not able to find any answer yet, the examples on "markers" are working with path properties like : style('marker-end', "url(#end-arrow)")
Firstly, the long answer from SO. The quick answer is SVG <markers>
The (basic) short answer: Take a point a little before the red dot, measure the slope and draw a line between the two points. Now the question is simplified to: How do add an arrow to the end of a straight line? Use the quick answer.
Add this to your code to visualize the answer:-
var pathPoint2 = pathEl.getPointAtLength(pathLength*0.78);
var point2 = svg.append("svg:circle")
.style("fill", "blue")
.attr("r", 3)
.attr("cx", pathPoint2.x)
.attr("cy", pathPoint2.y);
var slope = (pathPoint.y - pathPoint2.y)/(pathPoint.x - pathPoint2.x);
var x0 = pathPoint2.x/2;
var y0 = slope*(x0 - pathPoint.x) + pathPoint.y;
var line = svg.append("svg:path")
.style("stroke","green")
.attr("d", "M" + pathPoint.x + "," + pathPoint.y + " L" + x0 +","+ y0);

Resources