Somebody's got to have a smart idea for this.
I've got a great bar graph with some N/A values, like for Vermont. So no bar is drawn for Vermont. Great! But I'd like to append "N/A" just to the right of the word Vermont, or some kind of indication that Vermont is an N/A. Otherwise, it possibly looks like a mistake, like "Hey, where is Vermont's data? Why is there a blank spot here?"
http://bl.ocks.org/greencracker/raw/4f7ff98cea98413ef5f4/
Key block is this:
barUpdate.select("rect")
.attr("width", function (d) { return x(d[age]);}) //< -- need something here?
.attr("fill", function (d) {
if (d.State == "Georgia") {return "goldenrod";}
else {return color(age); } ;})
.attr("fill-opacity", function (d) {
if (d.State == "United States") {return 0.6;} ;});
I would think something like this … ?
.attr("width", function (d) { if (x(d[age])) == 0) {this.append("umm? something here?");}
{x(d[age]);}
I know I can't append "text" to a "rect". I've fiddled with this.append("g").append("text") but no luck.
I'm not sure that I would use N/A, as it can be ambiguous. Does it mean Not Applicable or Not Available. They are subtle differences, for sure, but it might be worth selecting a different phrase to indicate a lack of data.
That said... You can easily just add a new text element for each data item that you've got, and just set it to "" for the states that have data and "N/A" for those that don't.
I've forked your gist and come up with this: http://bl.ocks.org/benlyall/361716dbd79f13488a4e
The extra code that I've added is:
barEnter.append("text")
.attr("class", "value-label")
.attr("x", -3)
.attr("y", y.rangeBand() / 2)
.attr("dy", ".35em")
.attr("text-anchor", "start");
...
barUpdate.select("text.value-label")
.attr("x", function(d) {
return x(d[age]);
})
.text(function(d) {
if (d[age] == "") {
return "N/A";
} else {
return "";
}
});
You'll obviously end up with additional text elements in your SVG, it's up to you to decide if that matters to you or not.
Related
So, I want to render different symbols for node points on a tree graph. Which isn't too bad, I have the following code that can do that:
nodeEnter.append("path")
.attr("d", d3.svg.symbol()
.type(function(d) { if
(d.type == "cross") { return "cross"; } else if
(d.type == "rectangle") { return "rect";}
etc...
}));
The issue I have is, if you use append with a specific shape, for example append("circle"), you can specify the width, height, etc. With d3.svg.symbol, you can only specify the size. How can I dynamically use something like this:
nodeEnter.append("rect")
.attr("width", rectW)
.attr("height", rectH)
.attr("stroke", "black")
.attr("stroke-width", 1)
.style("fill", function (d) {
return d._children ? "lightsteelblue" : "#fff";
});
But also do it with dynamic shapes based on the node type attribute?
I tried something like:
nodeEnter.append(function(d){if
(d.type == "rectangle") { return "rect"; }});
However, this throws an error of:
TypeError: Argument 1 of Node.appendChild is not an object.
Most of the examples I have found while searching this don't bother trying to modify the symbol as long as they are all unique. Again, I want to be able to do something more complex.
Did not get any responses for this, but I was able to work something out. The answer is to use a raw input for the 'd' attribute and skip d3.svg.symbol altogether:
nodeEnter.append("path")
.attr("d", function(d) { if
(d.type == "circle") { return "M-40,0a40,40 0 1,0 80,0a40,40 0 1,0 -80,0";}
});
The caveat is, you need to draw your shapes manually with path.
Similar to question here, I've been working on the same thing as in the last question. My question now is similar to the link, but is more about the implementation. When I run my code, I get an error in my log that says "TypeError: x.ticks is not a function". This is the piece of code it refers to:
svg.selectAll("g.grid")
.data(y.ticks()).enter()
.append("g").attr("class", "grid")
.selectAll("rect")
.data(x.ticks())
.enter()
.append("rect")
.attr("x", function(d, i, j) {
return xScale(j);
})
.attr("y", function(d, i, j) {
return yScale(i);
})
.attr("width", xScale.rangeBand())
.attr("height", yScale.rangeBand())
.attr("fill", function(d, i) {
return ((i) % 2) == 1 ? "green" : "blue";
});
This code works perfectly well in this fiddle, but gives me an error while running it in my code, as seen here. Any help?
In your example you are trying to call ticks on an ordinal scale instead of a linear scale. Utilising rangeBoundBands, like in the question you've linked to, is probably the way to go.
Your problem is you're using an ordinal scale. D3 makes you use a linear scale if you want to call .ticks(), otherwise it throws an error
I'm trying to place labels using the larskotthoff block page http://bl.ocks.org/larskotthoff/11406992
The issue I'm having is where the labels translate is worked out on the line.
.attr("transform", function (d) {
return "translate(" + projection(d.geometry.coordinates[0][0][0]) + ")";
})
So in his guide he is not doing d.geometry.coordinates[0][0][0] but just d.geometry.coordinates. This works for him because he is not working out the labels for multi-polygons because when I tried without the [0][0][0]
So my labels are not where they should be as you can see in the following image.
Although I've managed to get the labels drawing the structure of my feature (in this case d) is different and not a single array of coordinates. It is a 3 dimension array. So for this reason the placement (translate) is happening based on the first element [0][0][0] of each dimension. How do I overcome this? I want it to work out its placement from the whole set of polygons. Does d3 make something available for this?
UPDATE: Solution (thanks to Lars)
.data(json.features)
.enter().append("text")
.attr("class", "place-label")
.attr("transform", function (d) {
console.log("test");
return "translate(" + path.centroid(d) + ")";
})
.attr("x", function (d) {
return path.centroid(d)[0] > -1 ? 6 : -6;
})
.attr("dy", ".35em")
.style("text-anchor", function (d) {
return path.centroid(d)[0] > -1 ? "start" : "end";
})
As Lars Kotthoff confirms in comment. To get the centre location from a node/feature you can use path.centroid as described in the d3 documentation below.
Path.centroid
I've created a plunk to demonstrate my problem.
The issue is that my .enter(),update(),exit() method is not working for my d3.chart.layout() visualization.
Instead, I get the classic "double post" problem. My keys are the same, however.
What I want to happen is for my d3 steam graph to update its data (such that the y values all go to 0, and the chart disappears). My data binding is coded normally:
var steam = svg.selectAll(".layer")
.data(layers, function(d){console.log(d); return d.key})
steam.enter().append("path")
steam.style("fill", function(d, i) { return z(i); }).style("opacity","0").transition().duration(400)
.style("opacity","1")
.attr("class", "layer")
.attr("d", function(d) { return area(d.values); })
steam.exit().transition().duration(500).remove()
What is happening/any ideas?
So I got it to work, though I'm still confused as to why it works this way. I needed to move the svg adding out of the update function and into the namespace (that was obvious).
But the update became this, with a transition() method. Can anyone help me understand where I went wrong?
test = svg.selectAll("path").data(layers, function(d){return d.key})
test.enter().append("path")
test.style("opacity",1).style("fill", function(d, i) { return z(i); })
.attr("class", "layer").transition()
.duration(2000)
.attr("d", function(d) { return area(d.values); });
test.exit().transition().duration(1500).style("opacity",0).remove();
So I've been picking up D3.js to visualize some data I have, and I need a bit of help understanding why certain things happen.
I took the code from this example on the D3 site, and modified it to read from a flat CSV rather than a flattened JSON file. Here's what it looks like:
d3.csv("top-brands.csv", function(csvData) {
var data = { name: "brand", children: csvData };
var node = svg.data([data]).selectAll(".node")
.data(pack.nodes)
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
node.append("title")
.text(function(d) { return d.brand; });
node.append("circle")
.attr("r", function(d) { return d.r; })
.style("fill", function(d) { return color(d.brand); });
node.append("text")
.attr("dy", ".3em")
.style("text-anchor", "middle")
.text(function(d) { return d.brand; });
});
It seems to work quite well, in the sense that my bubbles all get drawn with the correct data, but there are two niggling issues that keep coming up:
It keeps drawing a large circle that encloses the other circles. It appears to be as large as the canvas allows (480px by 480px) but I have no idea how to make it go away. Is it because of how my data is processed?
If I try to do anything with "d.brand" in any way, it will tell me that d.brand is undefined. The example from the D3 site takes the title and gets the substring if the length is too long, but I can't seem to do that. Is it because of the way I'm pulling my data?
Appreciate any help! I can't really provide example CSVs because it's proprietary data, but it's just three columns: brand, count, and proportion, which is a string, an int, and a float (percentage) in that order. Dummy data would work just fine, I think.
Thanks SO!