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.
Related
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
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.
I am trying to draw a pack layout in d3.js without outermost variable.
I want to draw a pack layout without outer most parent circle. Is there any way to do it?
Yes, there is. I would suggest following approach: you leave all circle pack initialization intact. You only change the point of code where circles are actually appended to DOM/SVG tree. I'll show this in a couple of examples. This jsfiddle is an example of "regular" circle pack:
The key code responsible for adding circles to the DOM tree is this:
var circles = vis.append("circle")
.attr("stroke", "black")
.style("fill", function(d) { return !d.children ? "tan" : "beige"; })
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", function(d) { return d.r; });
If one adds just this line between "vis" and ".append("circle")": (another jsfiddle available here)
.filter(function(d){ return d.parent; })
the root node will disappear:
If one adds this line:
.filter(function(d){ return !d.children; })
all nodes except leave nodes (in other words, those without children) will disappear:
And, a little bit more complex, this line
.filter(function(d){ return (d.depth > 1); })
will make the root parent circle and all its direct children disappear:
I have two g elements each containing circles. Circles are organized using force.layout. The g elements are transitioning.
You can see here: demo. Reduced code:
var dots = svg.selectAll(".dots")
.data(data_groups)
.enter().append("g")
.attr("class", "dots")
.attr("id", function (d) {
return d.name;
})
...
.each(addCircles);
dots.transition()
.duration(30000)
.ease("linear")
.attr("transform", function (d, i) {
return "translate(" + (150 + i * 100) + ", " + 450 + ")";
});
function addCircles(d) {
d3.select(this).selectAll('circle')
.data(data_circles.filter(function (D) {
return D.name == d.name
}))
.enter()
.append('circle')
.attr("class", "dot")
.attr("id", function (d) {
return d.id;
})
...
.call(forcing);
}
function forcing(E) {
function move_towards(alpha) {
...
}
var force = d3.layout.force()
.nodes(E.data())
.gravity(-0.01)
.charge(-1.9)
.friction(0.9)
.on("tick", function (e) {
...
});
force.start();
}
I need to move circle (for example id=1) from the first g element to the second one using transition.
Any suggestions are appreciated.
It can be done.
What I did was:
1) Use jquery to append the point to the target group
2) Use a transformation (no transition) to move the point back to its original location
3) Transition the point to its new location
The jQuery was used for the appendTo method. It can be removed and replaced with some pure Javascript stuff, but it's quite convenient.
I've got a partially working fiddle here. The green points work right, but something is going wrong with the blue ones. Not sure why.
In my view, transitions work on a single element. If an element changes its position in the DOM tree, from below one g to another, I can't think of a way to make that as one smooth transition because it's basically a binary split: Now there's an element under one g, now it's gone but there's another one somewhere else.
What I'd do in order to achieve what I think you want to do: Group everything under the same ´g´, assign color and translation individually, then change color and translation for that single element you want to change.
But don't take that as a reliable statement that you can't do it the way you originally wanted.