D3 Sunburst clip path of text - svg

I am trying to implement a d3 visualization based on the sunburst diagram, and i have found an almost perfect online example of this which i have got working http://tributary.io/inlet/4127332/:
My main issue is that I need to also Clip the text to the segment,I have tried using the svg clip path but my meager d3 skills have let me down. Any help with this is appreciated.

So my first attempt to clip the text did not work and I think this is because the arc's coordinate space does not line up with the text's coordinate space in the way that you want if you are using the arc generator, as you are.
I found that if I apply the clip to the groups you make for each node then it worked like a charm. There was one caveat. When I tried generating my clip path and then applying them the order that the nodes were joined to the elements differed and so the wrong path were clipping the wrong text. I got around this by adding an id to each data element. You can see the final version here
The important parts are adding the clip paths (note the use of the new id field):
svg.append('defs')
.selectAll("clipPath")
.data(partition.nodes)
.enter().append('svg:clipPath')
.attr('id', function(d,i) { return d.id;})
.append('path').attr('d', arc);
Then you simply have to reference them on your node groups (again using the id):
group =
svg.selectAll("g")
.data(partition.nodes)
.enter().append('svg:g')
.attr('clip-path', function(d,i) { return 'url(#' + d.id + ')';});
In the tributary I put the svg data join first so that the "defs" node would appear in the usual place (first after the svg tag), but I do not think this is technically necessary.

Related

fabricjs: Issue with multiple paths of SVG having the exact same gradient

For my web app, I am creating SVG elements in Illustrator and then using them in a library of elements that users can add to the fabric canvas.
Some elements are simple but some complex with multiple compound paths etc.
I have came across an unusual issue where if I create a path with a gradient fill, and then copy that path, save the SVG and add it onto the canvas, only the first path would have the gradient and the rest would be flat colors.
Here is a screenshot of what I mean...
After experimenting and trying different things, I finally discovered that this is happening because the paths have the exact same gradient properties.
So if the gradient slider (color stops, opacity, location etc.) of two or more paths have the exact same properties in Illustrator, then the issue occurs.
So the workaround is to alter something like the location (for example) to be 99.9% instead of 100% on the copied path, then the issue goes away. However, this will quickly become a tedious and annoying way to fix this. Basically, each path with a gradient, needs to have a unique gradient set up and cannot be identical to another paths gradient properties.
Here are more screenshots to better explain...
After making this change...
The first and second path's gradient's location are different.
The first, third, fourth and fifth paths have exact same gradient.
This is what it looks like when I add it to the canvas now...
Here is the code I am using to add the SVG to the canvas...
fabric.loadSVGFromURL(image, function(objects, options) {
var oImg = fabric.util.groupSVGElements(objects, options);
oImg.perPixelTargetFind = true;
oImg.targetFindTolerance = 4;
canvas.add(oImg);
canvas.renderAll();
});
Can anyone tell me why this is happening and if there is a way to fix this with code rather than Illustrator? I have hundreds of elements to create that will have many paths with the same gradients. I know it will be a real pain to have to worry about paths not having the exact same gradient.
http://jsfiddle.net/oc70xjsq/
Link to the SVG

Centering multiple line wrapping

My question is regarding how to center multiple elements in one node after line wrapping. I'm utilizing code example given here on word wrapping: http://bl.ocks.org/mbostock/7555321 to create multiple tspan objects.
After creating/drawing all the tspan objects, I define the location of the X and Y. First I get the text area of the entire tspan and create a box around it:
var tspanbbox = d3.select(this).select("tspan").node().getBBox();
var node_bbox = {"height": tspanbbox.height+5, "width": tspanbbox.width+5};
var rect = d3.select(this).select('rect');
rect.attr("x", -node_bbox.width/2).attr("y", -node_bbox.height/2)
rect.attr("width", node_bbox.width).attr("height", node_bbox.height+10);
and then I added the attribute for the tspan, the text that's within rect.
tspan.attr("x", -node_bbox.x).attr("y", -node_bbox.y);
This code correctly prints a box around the area around the text, but there's overflowing text.
http://imgur.com/zxiRmxR
So what I'm trying to do is to center the group as an entity rather than the first tspan (what I'm assuming is the first line after the GO term).
If I try to alter the tspan attribute (as shown in the code above), only one of the tspan objects move. If I do it with all of them using selectAll("tspan"), all of them the tspans are grouped on the same y axis.
Is there any way of going about this properly?
If anyone else is running into this issue, you can check the number of lines inputted total and make adjustments on the y attribute based on how many lines are implemented (so it's a little manual and archaic but it works).

Is it possible to translate an SVG path to GeoJSON?

My objective here is to allow users to specify territories or regions given a background world-map overlay, which is an SVG generated from GeoJSON data using D3. I have done the part where the territories' points are pinpointed by the user, and an SVG is generated. This works well.
Now I would like to save the territory's coordinates, using the background map's projection, scale and translation. I saw a lot of documentation about translating GeoJSON data to SVG s, but nothing about the other way. Is it even possible ?
Thanks Ben Lyall, eventually I used the native SVG functions getTotalLength() and getPointAtLength() to convert my path to an array of top/left positions (in pixels), then d3's projection.invert() to translate them into coordinates.

How do I rotate or scale (transform) an SVG path relative to its center point?

I'm trying to rotate and scale shapes within an SVG around their center point. I've looked into several libraries, including Jquery, Greensock, D3, RaphaelJS, but I haven't been able to find any that provide a straightforward way to accomplish this. Each animates the shape from the origin point (which I understand is the default). I want to be able to spin a shape around its center point or scale it up or down from the center point.
Here are a couple examples using Greensock and D3 that illustrate the default behavior: http://jsbin.com/AHEXiPa/1/edit?html,js,output
Each of these examples bounce in and out from the top left as opposed to remaining stationary and expanding from the center of the triangle out in all directions.
Can one of the libraries I mentioned accomplish this, or is there another library or method I should consider?
Ideally, I need to be able to apply the animation/transform to an existing object in the DOM. D3 is good at this for instance, but Raphael seems to require converting an SVG to Raphael first prior to injecting it into the DOM.
Really its a case of pick the library that suits your needs, and then you will figure a way. As BigBadaboom says, if you do a search, there are lots of solutions.
To try and combine your questions, as sometimes the tricky bit is using an existing DOM object, I've included an example in Snap.svg. You can often do something similar in most libraries.
jsfiddle here Fiddle using your existing html.
s = Snap("#mySVGContainer1"); // create a canvas from existing svg
var triangle1 = s.select("#myShape1").transform("r90"); //select&transform existing object
p = Snap("#mySVGContainer2");
var triangle2 = p.select("#myShape2");
var bbox = triangle2.getBBox(); //bounding box, centre cx/cy
//rotate and scale with transform string (raphael/snap format)
triangle2.animate({ transform: "r180," + bbox.cx + ',' + bbox.cy + "s3,3," + bbox.cx + "," + bbox.cy }, 2000);
For rotations, as #Ian points out, you can specify the center of rotation. For other transformations, changes are defined relative to the path's (0,0) point.
The easiest way to get transformations to work relative to the path's center is to either:
Define the path so that it is centered around the (0,0) point; or
Wrap the path in a <g> element, and then translate it so it is centered on the (0,0) point of the <g> element's coordinate system.
Then, you can apply rotations, scales and transforms (on the <g> element, if using) and they will all be nicely centred.
The trickiest part is figuring out the "center" of an arbitrary shape. #Ian's approach of using the center of the bounding box will usually give decent results. If your shape is a polygon there are d3 functions you could use.
Example showing a shape moving with the mouse, rotating and changing scale, all centered around the center of the bounding box:
http://fiddle.jshell.net/LgfE3/
Edit: simplier jsfiddle
I've been looking for a long time, and will settle for the following.
1. Design your svg shape at coordinate x:0,y:0.
2. Identify by hand the center of rotation, by example, center = [ x:50,y:100].
3. Build a spinIt() function such :
function spinIt() {
needle.transition()
.duration(2000)
.attrTween("transform", tween);
function tween() {
return d3.interpolateString("rotate(-180, 50, 100)", "rotate(90, 50, 100)");
}
}
4. Use it on a triger:
svg.on("click", spinIt);
http://jsfiddle.net/SHF2M/79/

RaphaelJs : How to add some text to an area defined by a path?

I have an SVG image of the united states which I've drawn with a little bit of help from Raphaël js:
http://jsfiddle.net/zCRkg/2/
What I want to do is place some text to the right of each state when you hover over it. The problem is that, since each state is a path, its quite difficult to determine the x & y coordinates for where to place the label.
So, does anyone know a way of calculating the centre of a path using Raphaël? If failing that anyone know how to do this given a an array of vectors?
What you're looking for is the getBBox function in Raphaël. It will give you a bounding box object that you can use to calculate the central point of the path:
var bbox = st.getBBox();
var text = r.text(bbox.x + bbox.width/2, bbox.y + bbox.height/2, "Foo");
I forked your fiddle and made it show a static text in the middle of each state on hover. Picking up the state name from your data is left as an exercise.
the average of the difference between each coord should give you the centre, but thats probably not the most efficient way

Resources