I use this code:
svg.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", function(d) { return domainOnlyScale(parseFloat(d.Size)+0.01); } )
.attr("cx", function(d) {return x(d.x+(Math.random() * 0.25) - 0.125); })
.attr("cy", function(d) { return y(d.y+(Math.random() * 0.25) - 0.125); })
.style("fill", function(d) { return color(d.color); })
.style("stroke-width", "1px")
.style("stroke", function(d) { return strokecolor(d.color); });
And everything works. All data points show up. Now I change this part of the code to:
console.log(data); // Shows all data points!
var groupings = svg.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("transform", function(d){return "translate("+(x(d.x+(Math.random() * 0.25) - 0.125))+","+y(d.y+(Math.random() * 0.25) - 0.125)+")"}); //used console.log here)
groupings.append("circle")
.attr("class", "dot")
.attr("r", function(d) { return domainOnlyScale(parseFloat(d.size)+0.01); } )
.style("fill", function(d) { return color(d.color); })
.style("stroke-width", "1px")
.style("stroke", function(d) { return strokecolor(d.color); });
groupings.append("text")
.attr("class", "bubbletext")
.attr("dx", function(d){ return -4 })
.attr("dy", function(d){ return +5 })
.text( function(d) { return d.category.substring(0,1); } );
I used another console.log inside the function of the transform of the groupings-creation, and the data is already filtered there. From a few tries it seems as if the first ~15 entries are missing.
Thanks in advance!
The problem is that you have g elements on the page already. These are getting selected through svg.selectAll("g") and then matched to data. Hence, the enter selection doesn't contain all the elements you expect to be there.
The fix is simple -- assign a class to those g elements you're using here to be able to distinguish them from the rest and select accordingly:
svg.selectAll("g.dot")
.data(data)
.enter()
.append("g")
.attr("class", "dot");
Related
I have 2 lines in the form of waves plotted in x-y axis based on randomly generated data and i am showing circles on the waves denoting the data points on it.
Based on setInterval of 200 ms, I am updating the original data and the lines(waves) are moving to the left, but the issue is that the only circles which are there in the initial interval are moving and for 2nd interval onward the circles are not showing up on the waves.
see the jsfiddle for the running code : https://jsfiddle.net/rajatmehta/tm5166e1/10/
here is the code :
chartBody.append("path") // Add the valueline path
.datum(globalData)
.attr("id", "path1")
.attr("class", "line")
.attr("d", valueline);
chartBody.selectAll(null)
.data(globalData)
.enter()
.append("circle")
.attr("class", "dot1")
.attr("r", 3)
.attr("cx", function(d) {
console.log(d);
return x(d.timestamp);
})
.attr("cy", function(d) {
return y(d.value);
});
chartBody.selectAll(null)
.data(globalDataNew)
.enter()
.append("circle")
.attr("class", "dot2")
.attr("r", 3)
.attr("cx", function(d) {
return x(d.timestamp);
})
.attr("cy", function(d) {
return y(d.value);
});
chartBody.append("path") // Add the valueline path
.datum(globalDataNew)
.attr("id", "path2")
.attr("class", "line")
.attr("d", valueline2);
any idea how to do that ?
You need to create new circles based on the updated data. Currently, you are only updating the data to selection, but not appending circles, and then moving existing circles to the left.
For example, you could to this:
chartBody.selectAll(".dot1")
.data(globalData, function(d){ return d.timestamp; })
.enter()
.append("circle")
.attr("class", "dot1")
.attr("r", 3)
.attr("cx", function(d) {
return x(d.timestamp);
})
.attr("cy", function(d) {
return y(d.value);
});
chartBody.selectAll(".dot2")
.data(globalDataNew, function(d){ return d.timestamp; })
.enter()
.append("circle")
.attr("class", "dot2")
.attr("r", 3)
.attr("cx", function(d) {
return x(d.timestamp);
})
.attr("cy", function(d) {
return y(d.value);
});
d3.selectAll(".dot1")
//.data(globalData)
.transition()
.duration(duration)
.ease("linear")
.attr("transform", "translate(" + String(dx) + ")");
d3.selectAll(".dot2")
//.data(globalDataNew)
.transition()
.ease("linear")
.duration(duration)
.attr("transform", "translate(" + String(dx) + ")");
See here: https://jsfiddle.net/tm5166e1/11/
This appends the data, using the timestamp as a key so you only create new circles for newly added datums.
(There is an issue when they are first added which is beyond the scope of this question, but it will be worth checking out these examples: https://bl.ocks.org/tomshanley/15a2b09a95ccaf338650e50fd207fcbf and https://bl.ocks.org/mbostock/1642874)
I'm trying to figure out the correct way to displays labels that will sit on top of each bar in my bar chart. I'd also like them to display a % after the number.
Here is my Plunker: https://plnkr.co/edit/FbIquWxfLjcRTg7tiX4E?p=preview
I experimented with using this code from the question found in the link below. However, I wasnt able to get it to work properly (meaning the whole chart failed to display)
Adding label on a D3 bar chart
var yTextPadding = 20;
svg.selectAll(".bartext")
.data(data)
.enter()
.append("text")
.attr("class", "bartext")
.attr("text-anchor", "top")
.attr("fill", "white")
.attr("x", function(d,i) {
return x(i)+x.rangeBand()/2;
})
.attr("y", function(d,i) {
return height-y(d)+yTextPadding;
})
.text(function(d){
return d;
});
This is the most straight forward way given your existing code:
// keep a reference to the g holding the rects
var rectG = g.append("g")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d) { return "translate(" + x0(d.State) + ",0)"; })
.selectAll("rect")
.data(function(d) { return keys.map(function(key) { return {key: key, value: d[key]}; }); })
.enter();
// append the rects the same way as before
rectG.append("rect")
.attr("x", function(d) { return x1(d.key); })
.attr("y", function(d) { return y(d.value); })
.attr("width", x1.bandwidth())
.attr("height", function(d) { return height - y(d.value); })
.attr("fill", function(d) { return z(d.key); });
// now add the text to the g
rectG.append("text")
.text(function(d){
return d.value + '%';
})
.attr("x", function(d) { return x1(d.key) + (x1.bandwidth()/2); })
.attr("y", function(d) { return y(d.value); })
.style("text-anchor", "middle");
Updated plunker.
I am trying to add label to the nodes in a D3 force layout, but there seems to be some issue with it. All the text just shows up on top of the screen, overlapping on each other. Here is the code snippet:
var link = g.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line");
var node = g.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("r", 2.5)
.on('click', clicked);
var text = g.append("g")
.attr("class", "labels")
.selectAll("text")
.data(graph.nodes)
.enter().append("text")
.attr("dx", 12)
.attr("dy", ".35em")
.text(function(d) { return d.name });
Elements piling up on the top left corner of the SVG (which is the origin) is a symptom of several problems, among them:
NaN for the positions
Not setting the positions
Not translating the element
Not including the element in the tickfunction
As you have a force directed chart, the last one is the obvious reason.
Solution: You have to include the text in your ticked function.
function ticked() {
link.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; });
text.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; });
}
I have a D3 area chart showing values over time. Along my x-axis, I have 1-7 relating to days of the week. I want to replace these with mo, tu, we, etc etc etc
this.g.selectAll(".xLabel")
.data(_this.x.ticks(6))
.enter().append("svg:text")
.attr("class", "xLabel")
.text(String)
.attr("x", function(d) { return _this.x(d) })
.attr("y", 0)
.attr("text-anchor", "end")
.attr("stroke", "white")
.attr("opacity", .5);
I thought this could be easily achieved by replacing
.attr("x", function(d) { return _this.x(d) })
with
.attr("x", function(d) { return _this.x(_this.arrDays[d]) })
(with _this.arrDays being a simple ["mo", "tu",......] array) but this is giving me the following error:
Error: Invalid value for attribute x="NaN"
Anyone care to shed some light?
Sorted with:
this.g.selectAll(".xLabel")
.data(_this.x.ticks(6))
.enter().append("svg:text")
.attr("class", "xLabel")
.text(function(d) {return _this.arrDays[d-1]}) //changed this line
.attr("x", function(d) { return _this.x(d) })
.attr("y", "0")
.attr("text-anchor", "end")
.attr("stroke", "white")
.attr("opacity", .5);
This is a very basic question, but how do I access the value of attributes in d3?
I just started learning today, so I haven't figured this out yet
Suppose I have this as part of my code here
http://jsfiddle.net/matthewpiatetsky/nCNyE/9/
var node = svg.selectAll("circle.node")
.data(nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", function (d) {
if (width < height){
return d.count * width/100;
} else {
return d.count * height/100;
}
})
.on("mouseover", animateFirstStep)
.on("mouseout",animateSecondStep)
.style("fill", function(d,i){return color(i);})
.call(force.drag);
For my animation the circle gets bigger when you mouse over it, and I want the circle to return to its normal size when you move the mouse away. However, i'm not sure how to get the value of the radius.
i set the value here
.attr("r", function (d) {
if (width < height){
return d.count * width/100;
} else {
return d.count * height/100;
}
I tried to do node.r and things like that, but i'm not sure what the correct syntax is
Thanks!
You can access an attribute of a selection with:
var node = svg.selectAll("circle.node")
.data(nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", function (d) { return rScale(d.count); })
.on("mouseover", function(d) {
d3.select(this)
.transition()
.duration(1000)
.attr('r', 1.8 * rScale(d.count));
})
.on("mouseout", function(d) {
d3.select(this)
.transition()
.duration(1000)
.attr('r', rScale(d.count));
})
.style("fill", function (d, i) {
return color(i);
})
.call(force.drag);
in this context, this points to the DOM element binded with d. Normally, the area of a circle must be proportional to the quantities that you are showing, take a look at the documentation of Quantitative Scales. A fork of your fiddle is here.