Having issue with translate function - d3.js

I have coordinatesX and coordinatesY arrays. For example if I want to draw an arc between coordinatesX[1] and coordinatesY[4], a part of the code goes :
svg.append("path")
.attr("d", arc)
.attr("fill", "red")
.attr("transform", "translate(coordinatesX[1],coordinatesY[4])");
I am having problem with translate function. It says :
Error: Invalid value for attribute transform="translate(coordinatesX[1],coordinatesY[4])"
How can I overcome this problem?
Thanks in advance.

It has to be a single string. As Salvador pointed out in the comments, in your example you were using coordinatesX[1] etc literally. But, if you concatenate, JavaScript creates a single string for you (if you add a number to a string, the result will be a string). In your case:
.attr("transform", "translate(" + coordinatesX[1] + "," + coordinatesY[4]) + ")");

Related

How to dynamically translate an SVG group in D3.js?

I'am working on a bubble-chart using d3. Now there should be an arrow as a graphical asset below the text elements. What I wan to achieve is a dynamic positioning of the arrow-group having a defined gap between itself and the last text:
I've already tried to position it with a percentage value:
arr.append("g").attr("class", "arrow").style('transform', 'translate(-1%, 5%)');
Which does not give the effect I want to.
I also tried to insert a dynamic value based on the radius of the circles which is simply not working and I don't know why:
arr.append("g")
.attr("class", "arrow")
.attr('transform', function(d, i) { return "translate(20," + d.r / 2 + ")");});
OR
arr.append("g")
.attr("class", "arrow")
.style('transform', (d, i) => 'translate(0, ${d.r/2})');
Please find my pen here.
Thank you very much in advance!
Ok.. solved it! For everyone who is interested or having the same trouble:
My last attempt was nearly correct but I was not able to transform via .style(...). I had to use .attr(...) like this:
arr.append("g")
.attr("class", "arrow")
.attr('transform', (d, i) => translate(0, ${d.r/2})');

Add Dynamic Title to Axes in D3

I'm using the following code:
// Add title to y axis
svg.append("text")
.attr("x", -500)
.attr("y", -100)
.attr("text-anchor", "left")
.attr("transform", "rotate(270)")
.style("font-size", "16px")
.text("Test");
How can I have the text to be derived from the result of a function instead of being a string literal? Any help would be appreciated. Thanks.
You can just pass in a function .text, e.g. to set the text value to be the time at execution:
.text(function(){ return new Date(); })
If you want the text value to depend on data, then you need to bind data to the text element first.

D3 tooltip with dotted x/y axis lines, can't create multiple tooltips

I am trying to create a tooltip for my line chart that sends out dotted lines to the x and y axis (identical to this d3 n00b example, but my chart has multiple lines) http://www.d3noob.org/2014/07/my-favourite-tooltip-method-for-line.html
I have tried setting up two focus groups, tried adding the extra line info into the existing group, but all I can get is the date running on both lines but the dotted lines and data info only work on one.
Any help would be gratefully accepted.
Here is the chart with code underneath:
http://bl.ocks.org/anonymous/d1dbc221f95f6308b351
This is now fixed, renaming the two focus groups worked
Correct code:http://bl.ocks.org/anonymous/b77e904f34aa2162e1df
You are using the same variable for the two focus groups:
var focus = svg.append("g") // tooltip
.style("display", "none");
var focus = svg.append("g") // tooltip
.style("display", "none");
Name one variable focus1 and the other focus2 to make sure that they remain separate.
I would also recommend refactoring the code such that you don't have to call the exact same functions on both of these, once for the CPI line and once for RPIJ line. Instead, you should create a function which calls the required functions on a focus. You should pass the function which line to attach to and the correct focus group, i.e. focus1 and focus2.
Update for the edit:
As I understand it, you have now added text.y1, text.y2, text.y3 and text.y4 to both the focus groups and are effectively hiding the unnecessary information by setting the correct strokes to #fff here:
// place the value at the intersection RPIJ
focus2.append("text")
.attr("class", "y1")
.style("stroke", "white")
.style("stroke-width", "3.5px")
.style("opacity", 0.8)
.attr("dx", 8)
.attr("dy", "-.3em");
focus2.append("text")
.attr("class", "y2")
.attr("dx", 8)
.attr("dy", "-.3em");
// place the date at the intersection RPIJ
focus2.append("text")
.attr("class", "y3")
.style("stroke", "white")
.style("stroke-width", "3.5px")
.style("opacity", 0.8)
.attr("dx", 8)
.attr("dy", "1em");
focus2.append("text")
.attr("class", "y4")
.attr("dx", 8)
.attr("dy", "1em");
However, while doing it you have committed a couple of unique mistakes in each case.
For focus1, you do not create a text.y4 at all while for focus2, you update text.RPIJ instead of text.y3:
focus2.select("text.RPIJ")
.attr("transform",
"translate(" + x(d.date) + "," +
y(d.RPIJ) + ")")
.text("£" + d.RPIJ);

dc.js accessing a chart other that it's own in postRender

I have 2 charts on a page (A and B) and wish for some custom behavior to be added to chart B when a brush filter on chart A is performed.
I thought I could achieve this by doing something like;
charta.on('postRender', function(){
...
d3.selectAll('#chartb svg')
.data(nested, function(d){ return d.key})
.enter()
.append("g")
.attr("class", "aclass")... more code....
But this #chartb selector doesn't seem to work - when I inspect the DOM it has appended the <g> attributes to the <html> element and not the svg element I wanted to append to.
Is what I am trying to achieve possible?
If you are just adding stuff to the other chart, something like this should be possible. I don't think you will be able to select the generated items of the other chart and then apply a d3 join to it, because it is already joined.
I believe the problem with the code above is that d3.select is what you use to choose the context for a join, and d3.selectAll is what you use to actually make the data join. See
http://bost.ocks.org/mike/join/
So your code is trying to join to the chart and svg elements, which would have the effect you are describing. Instead you'll want to d3.select the svg and then d3.selectAll the elements you want to add - even though they don't exist yet! Yes, it's kind of a mind-bender; take a look at the above and the linked articles to get a better idea of it.
Note: there are dc convenience methods on the chart object which will execute the selects in the right context.
I got this working in the end by replacing the .enter() with repeated calls to datum() instead. A bit of a hack, but it works; If anyone can suggest a more d3ish way of acheiving this, I would be very grateful.
var svg = chart.svg();
nested.forEach(function(withValues) {
_(withValues.values).filter(function(d){return d.value < threshold}).forEach(function(timesMatchingThreshold){
svg.datum(timesMatchingThreshold)
.append("rect")
.style("opacity", 0.6)
.attr("transform", "translate(" + 30 + ", " + (-30) + ")")
.attr("class", "belowThreshold")
.attr("x", function(d) {return x(d.date)})
.attr("y", function(d) {return 200 - y(d.value)})
.attr("width", 3)
.attr("height", function(d) {return y(d.value)});
});

How to put a break point in text in d3.js

I am drawing a piechart and i would like to have the labels with a break point. The labels are status and percentage which i get from csv file.
var g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc")
g.append("path")
.attr("d", arc)
.style("fill","#FFFFFF")
.transition()
.ease("bounce")
.duration(2000)
.delay(function(d, i) {return i * 1000;})
.style("fill", function(d) {return color(d.data.Source);});
g.append("text")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.style("text-anchor", "middle")
.html(function(d) {
if (eval(d.data.Components) >0)
{
return ((d.data.status) + " </br> " + " " + d.data.Percentage + "%");
}
});
An alternative to the tspan and text elements is the foreignObject. You can append a foreignObject, then append normal HTML. For you it would like something similar to this:
g.append("foreignObject")
.attr("width", "100px")
.attr("height", "100px")
.append("xhtml:div")
.html(function(d) {
return ((d.data.status) + " <br> " + " " + d.data.Percentage + "%");
});
I've found this approach easier to work with than tspan and text elements in the past, as it doesn't involve appending and positioning more elements than necessary.
The text element does not allow br tags in it.
From the docs:
Each ‘text’ element causes a single string of text to be rendered. SVG performs no automatic line breaking or word wrapping. To achieve the effect of multiple lines of text, use one of the following methods:
The author or authoring package needs to pre-compute the line breaks and use multiple ‘text’ elements (one for each line of text).
The author or authoring package needs to pre-compute the line breaks and use a single ‘text’ element with one or more ‘tspan’ child
elements with appropriate values for attributes ‘x’, ‘y’, ‘dx’ and
‘dy’ to set new start positions for those characters which start new
lines. (This approach allows user text selection across multiple lines
of text -- see Text selection and clipboard operations.)
Express the text to be rendered in another XML namespace such as XHTML [XHTML] embedded inline within a ‘foreignObject’ element. (Note:
the exact semantics of this approach are not completely defined at
this time.)
In your case, for example, you'll have to put d.data.status and d.data.Percentage + "%" in two different tspans in the text element and manually specify dy on them to align them vertically, like the examples shown here.

Resources