Focus+Context via Brushing for scatter plot in d3 - svg

How can I make dots responsive to the chart changes via brushing? I've tried to use tranfsorm attribute but it doesn't work good. Here is my fiddle. Coffescript is below
# draw dots
dots = focus.selectAll(".dot")
.data(bubbleData)
.enter().append("circle")
.attr("class", "dot")
.attr("r", (d) -> 2*Math.abs(d.surprise))
.attr("cx", xMap)
.attr("cy", yMap)
.style("fill", (d) -> color(d.surprise))
brushed = ->
xScale.domain (if brush.empty() then xScale2.domain() else brush.extent())
focus.select("._x._axis").call xAxis
focus.select(".line1").attr("d", line1(data))
focus.select(".line2").attr("d", line2(data))
focus.selectAll(".dot").attr "transform", (d, i) ->
"translate(" + xScale(d.date) + "," + yLeftScale(d.price) + ")"

Just like with the lines, you need to redraw the circles on brush:
focus.selectAll(".dot")
.attr("cx", xMap)
.attr("cy", yMap)
Complete demo here.

Related

D3 proper text() in tremap

i'm new to d3. My problem is unreadable text.
I suppose it's cuz i added text not to rect but to svg. How do i recode or fix this thing?
https://codepen.io/DeanWinchester88/pen/xxrjLrM
svg
.selectAll("text")
.data(root.leaves())
.enter()
.append("text")
.attr("x", (d) => d.x0 +10)
.attr("y", (d) => d.y0 + 20)
.text( (d) => d.data.name)
.attr("font-size", "12px")
.attr("fill","white")
.attr("overflow", "hidden")
.attr("max-width", (d) => d.x1 - d.x0)
tspan = text.text(null)
.append("tspan")
.attr("x", x)
.attr("y", y)
.attr("dy", dy + "em")
Here's your revised code based on my comment above:
https://codepen.io/mattsrinc/pen/MWozBWQ
svg
.selectAll("text")
.data(root.leaves())
.enter()
.append("text")
.attr('transform', d => 'translate(' + d.x0 + ',' + d.y0 + ')')
.selectAll('tspan')
.data(d => d.data.name.split(/(?=[A-Z][^A-Z])/g))
.enter()
.append('tspan')
.attr('font-size', '0.8em')
.attr('fill', 'white')
.attr('x', function(d) { console.log(d); return '0.5em' })
.attr('y', (d, i) => 12 * (i + 1))
.text(d => d);
I've left the console.log command so that you can see how the (game) name is split into parts. And that Pac-Man is right but it's the only one game for the console category (2600) so it translates to thin black rectangle on top left (something you should consider how to solve it visually).
Then also SVG CSS attribute "overflow: hidden" won't work for your data cells with TSPAN. You would have to handle that with calculating this cell width and width of the current word(s) in a line (of TSPAN tag to add). It would be better to follow and expand your other referenced codepen to work with cells that would ease this task.
Other than that I've just changed the color palette used and you can see color ranges here:
https://github.com/d3/d3-scale-chromatic

How to sort bar charts synchronous with the ticks of x axis

I am adopting the example from Mike Bostock to sort bar charts - http://bl.ocks.org/mbostock/3885705.
However, the transition of the bars and the ticks of the x axis are not occuring at the same time.
The reason is that the transition is called separately for the bars and the ticks (in the function change():
transition.selectAll(".bar")
.delay(delay)
.attr("x", function(d) { return x0(d.letter); });
transition.select(".x.axis")
.call(xAxis)
.selectAll("g")
.delay(delay);
The solution should be when constructing the svg, I would like to add each bar to the corresponding tick.
However I didn't succeed to correctly adjusting the line
svg.selectAll(".bar")
.data(data)
.enter().append("rect")...
e.g.
svg.selectAll("g.tick")
.data(data)
.enter().append("rect")...// would put all the bars under each g.tick
or
svg.selectAll(".x.axis")
.data(data)
.enter().append("rect")...// would put the bars after all g.tick's at the end. So again I can't grap the whole group together later.
Interesting idea.
You can get the rects grouped with each tick's g by:
svg.selectAll(".x>.tick")
.data(data)
.append("rect")
.attr("class", "bar")
.attr("x", function(d) {
return -x.rangeBand()/2; // set x to be half width, tick g will position it
})
.attr("width", x.rangeBand())
.attr("y", function(d) {
return -(height - y(d.frequency)); // set y to negative
})
.attr("height", function(d) {
return height - y(d.frequency);
});
You then have to rewrite the transition since calling .call(xAxis); will recreate the tick g and remove the bars.
transition.selectAll(".x>.tick")
.delay(delay)
.attr("transform", function(d) {
return "translate(" + x0(d.letter) + ",0)";
});
Putting this together.

.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");

How to translate svg g elements to cozy up with siblings?

var boxes = [[30, 45], [50, 30], [40, 30]]; // [w, h]
//Should i calculate `translate` value here, adding all heights?
var secs = wrapper.selectAll('g.section')
.data(data)
.enter()
.append('g')
.attr('class', 'section')
.attr("transform", "translate(" + 0 + "," + 'unknown' + ")");
//could be many sub-secs by a lot of data transformation before appending rect to the last one of them.
secs.append('rect')
.datum(function(d){return d;})
.attr('class', 'fragment')
.attr('x', 0)
.attr('y', 0)
.attr('width', function(d){return d[0];})
.attr('height', function(d){return d[1];})
// or here, not from data but from elem's dimensions?
secs.each(function(sec, i){
var prev = this.previousSibling?this.previousSibling.getBBox():'';
var ty = prev?prev.height+ prev.y:0;
d3.select(this).attr("transform", "translate(" + 0 + "," + ty + ")");
});
Is this how you translate g elements to fit their childrens, at any level of depth?
And i'll have to translate them manually when child expands?
i'm new at svg and d3.
Thank you.
It looks like you would need to keep track of the sum of heights and translate by that. That is, for each rectangle that you add add its height to a total which you can then use to offset subsequent elements.

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