SVG pie chart pull text to top - javascript

I have a SVG pie chart that is being auto generated by code. I want to ensure the text always shows up, however it is getting overlapped by color fills in the pie chart. I've tried using render-order to fix this to no avail. What can I do to get the equivalent of z-index in a SVG?
Here is the example code. Copy/paste and you'll see the problem.
<svg width="180" height="180"><g transform="translate(90,90)"><g class="slice"><path fill="#D0D0D0" d="M-39.049536520580226,81.08719811121772A90,90 0 0,1 -87.74351209636413,20.026884056068305L0,0Z"></path><text transform="translate(-35.18241671106134,28.05704108364301)" text-anchor="middle" render-order="109">Black - 14%</text></g><g class="slice"><path fill="#CEECC6" d="M-87.74351209636413,20.026884056068305A90,90 0 0,1 -70.36483342212269,-56.114082167286L0,0Z"></path><text transform="translate(-40.871756048182064,-10.013442028034143)" text-anchor="middle" render-order="300">Green - 14%</text></g><g class="slice"><path fill="#FFA8A8" d="M-70.36483342212269,-56.114082167286A90,90 0 0,1 -1.6532185776602093e-14,-90L0,0Z"></path><text transform="translate(-19.524768260290124,-40.54359905560886)" text-anchor="middle" render-order="109">Red - 14%</text></g><g class="slice"><path fill="#FFE8A1" d="M5.510728592200698e-15,-90A90,90 0 1,1 -39.049536520580226,81.08719811121772L0,0Z"></path><text transform="translate(43.871756048182064,10.013442028034147)" text-anchor="middle" render-order="109">Yellow - 57%</text></g></g></svg>

There's no z-index or render-order in SVG currently, although this is planned for the next version of the specification (SVG 2) which is currently being written by w3c. It uses painter's model i.e. whatever you put in the file last is drawn on top like a painter always paints on top of what's gone before.
If you want the text to be on top then you must add it to the SVG file after anything that it may overlap.

Related

How do I add polylines to my simple svg donut chart? [NO LIBRARIES]

I want to build a simple svg donut chart, with labels and polylines connecting the sectors to the label text like this.
I know this is possible using d3.js like implemented here, but I am restricted to using a simple chart without any libraries.
This is the code I have now;
<div class="wrapper">
<svg viewBox="0 0 42 42" preserveAspectRatio="xMidYMid meet" class="donut">
<circle class="donut-hole" cx="21" cy="21" r="15.91549430918954" fill="#fff"></circle>
<circle class="donut-ring" cx="21" cy="21" r="15.91549430918954" fill="transparent" stroke="#d2d3d4" stroke-width="3"></circle>
<circle class="donut-segment" data-per="20" cx="21" cy="21" r="15.91549430918954" fill="transparent" stroke="#49A0CC" stroke-width="3" stroke-dasharray="50 50" stroke-dashoffset="25"></circle>
<circle class="donut-segment" data-per="30" cx="21" cy="21" r="15.91549430918954" fill="transparent" stroke="#254C66" stroke-width="3" stroke-dasharray="50 50" stroke-dashoffset="75"></circle>
<!-- stroke-dashoffset formula:
100 − All preceding segments’ total length + First segment’s offset = Current segment offset
-->
</svg>
</div>
Any tips on how to draw polylines and position them properly without overlap?
EDIT: I want to input dynamic data to the component so that it will draw the chart whenever the data is changed.
implementations in d3: impl1
My main doubt was calculating the points for the arcs that have to be drawn for a donut chart, which I then understood thanks to this amazing answer by enxaneta.
All I had to figure out was adding the third point for the polyline, if the text.xCoordinate was closer to either side of the svg, I moved it either left or right by a preset amount. I also had to split the labels into multiple <tspan>s as <text> elements would not break the long text and long labels would get clipped off by the SVG. It is also possible to append HTML within a <foreignObject> and position it correctly, to overcome the text wrapping issue.
Another option is to use <path> elements for generating arcs, but I am not sure how to calculate the centroid of each of the arcs.
Recommended reading:
Medium article for <circle> donut chart kinda like enxaneta's answer, with the attributes explained.
Codepen with another <circle> donut chart
<path> donut chart as mentioned above, implemented beautifully by Mustapha.

d3.js extract coordinates of SVG path elements

After spending quite some time on this problem without any success, I decided to post my (first!) question here.
For a project I used topojson to plot part of a map. In my SVG I have some groups containing some points (which I placed using the geojson.io tool).
The map looks like this: plotted map
The structure of the code looks like this:
<svg width='1300' height='900'>
<g class="gate" id="D5">
<path d="M645.3426080089994,434.9821086442098m0,4.5a4.5,4.5 0 1,1 0,-9a4.5,4.5 0 1,1 0,9z"></path>
</g>
<g class='gate' id='d4'>
<path d="M605.3552137160441,383.2755208062008m0,4.5a4.5,4.5 0 1,1 0,-9a4.5,4.5 0 1,1 0,9z"></path>
</g>
</svg>
What I'd like to do is append some SVG based on some condition on the exact location of these points. Now while I know how to append SVG's, I'm having trouble extracting the coordinates of the point on the map. The SVG that I would like to append must be placed upon the points in the map. However, when for example I append a circle to the group, the coordinates that I enter are not considered relative to the point, but rather absolute to the whole SVG area. The circle ends up in the upper left corner instead of on the actual point.
I attempted to extract the coordinates of each point in order to use this data for plotting, but I have not yet succeeded. I managed to get the node by using:
d3.selectAll('#D5').select('path').node()
Which returns:
<path d="M605.3552137160441,383.2755208062008m0,4.5a4.5,4.5 0 1,1 0,-9a4.5,4.5 0 1,1 0,9z"></path>
When I look inside the path object, I see that deep inside the __ data__ element there are some coordinates, but I'm not able to extract these.
This issue wouldn't be a problem if I defined the coordinates for the point at start. However, due to the fact that this is topojson, the whole image is just a composition of path elements.
How can I plot a SVG on the points without explicitly defining coordinates beforehand? If any more information is required, please let me know.
Thanks!
What you are looking for is the getBBox() function
d3.selectAll('#D5').select('path').node().getBBox()
// { x: 640.8425903320312, y: 430.48211669921875, width: 9, height: 9 }
See https://jsfiddle.net/7zahj9gt/

Highcharts Grid Line Width (or Color) - Single Line

I have the following Highcharts (http://www.highcharts.com) scatter chart. Note that the axes start at -10 and stop at 10, with 0 in the middle. I want each 0 line to be a different width or color from all the others. Should be easy but I can't find in the API documentation how to do. All I can find is how to change properties for all lines. Has anyone done this or have suggestions?
Outside of the API I looked into using Javascript (specifically jQuery) to update the element's style but the only specific part (d) I could easily use to find which line to update isn't always the same. The element looks like.....
<path stroke-width="10" opacity="1" stroke="#D8D8D8" d="M 293.5 72 L 293.5 502" fill="none"></path>
Thanks.
You can implement plotLines. Check this out.
In yAxis, I have set a green line at value of 70.
In xAxis, I have set a blue line at value of 170.
You may play around with the color and width as well.

SVG Scaling and Coordinates

This is my first SVG project, and I’m not a programmer, but I dabble in interactive infographics. My previous experience in this area comes from working with ActionScript.
I’m using plain SVG (no Raphael, D3, etc.) and trying to create an interactive barchart. After some initial difficulty with the SVG coordinate system and scaling, I found some code online that handles the postscaling translation:
<text x="x_coord0" y="y_coord0" transform="scale(x_scale, y_scale) translate(-x_coord0*(x_scale-1)/x_scale, -y_coord0*(y_scale-1)/y_scale)" …>text</text>
And I converted it into this JavaScript:
var translationfactor = ((0 - y_position)*(y_scalefactor - 1) / y_scalefactor);
var matrix = "scale(1," + y_scalefactor + ") translate(0," + Number(translationfactor) + ")";
targetbar.setAttribute("transform", matrix);
The problem is that I need the bars “translated” back to the chart’s baseline, not the original locations of their topmost points. Currently the correctly scaled bars are hugging the top of the chart:
http://billgregg.net/miscellany/upsidedown-barchart.png
I’ve tried several fixes, including plugging the bars’ ”missing height” into translationfactor (the bars start out the full height of the chart and get scaled down dynamically). Nothing has worked. Part of my problem is that, besides being new to SVGs, I can stare at that code all day and my brain still can’t parse it. Multiplying negative numbers is too abstract and at a fundamental level I just don’t “get” the math, which of course makes modifying the code difficult.
My questions:
(1) What’s the fix for the code above to position the bars back on the baseline of the chart?
(2) Is there a more transparent, more pedestrian way of accomplishing the translation? My first thought along these lines was that if a bar’s height is reduced to 40% of its original value, then multiplying the original Y coordinate value by 250% should reset the bar to its original location (at least its topmost point), but that doesn’t seem to work.
(3) Is there a way to set a bar’s point of origin to its bottom? In Flash it’s possible, though as far as I know it’s a manual, not a programmatic task.
(4) Is there a method similar to .localToGlobal() in ActionScript that would allow me to avoid having to mess with the local coordinate system at all?
Behind the scenes there is matrix math going on and it can be hard to get your head around the pre and post multiplication of arrays.
It's not entirely clear what you are trying to achieve, but reading between the lines, it sounds like you are wanting to provide graph coordinates in their raw(ish) form and have the SVG scale and position them for you(?)
If that's the case, then I think the solution is simpler than what you think.
Assuming I'm right, we'll start with something that looks like this:
<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<g transform="">
<rect x="0" width="1" height="5" fill="red"/>
<rect x="1" width="1" height="11" fill="green"/>
<rect x="2" width="1" height="12" fill="orange"/>
<rect x="3" width="1" height="8" fill="blue"/>
</g>
</svg>
Where x is obvious and the bar length is in height. y defaults to 0, so we don't need it here.
You basically want to know what goes in the transform to scale and position the bars on your page. The fact that your graph is "upside-down" helps a little. Because the origin in an SVG is at the top left.
First apply a scale. Let's make the bars 20 pixels wide, and scale the lengths up by 10.
<g transform="scale(20,10)">
Next you want to position the graph on the page. Let's put the top-left corner at (40,40).
In SVG the transformations are concatenated in order (post-multiplied) so in order for the translation to be what you specify and not be multiplied by the scale, you should put it first.
<g transform="translate(40,40) scale(20,10)">
So the final SVG looks like this:
<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<g transform="translate(40,40) scale(20,10)">
<rect x="0" width="1" height="5" fill="red"/>
<rect x="1" width="1" height="11" fill="green"/>
<rect x="2" width="1" height="12" fill="orange"/>
<rect x="3" width="1" height="8" fill="blue"/>
</g>
</svg>
The above has been simplified by assuming you have already subtracted the values from your base 20%. If you wanted to keep the pure raw values, it's possible, but things get a bit trickier. You would need to either tinker with both the y and height value of each bar, or use clipping to hide the part of the bar above 20%.
For "right way up"/normal graphs. All you need to do is make the y scale negative and translate the graph so that the bottom-left is where you want it.
<g transform="translate(40,140) scale(20,-10)">
Hope this helps.

Trying to create a re-usable text-box (text with a square background-colour) in SVG 1.1?

I'm trying to create (what I thought would be!) a simple re-usable bit of SVG to show three lines of text, with a background colour - to simulate a 'post-it' note.
I have found some useful code here to get the Bounds of the Text http://my.opera.com/MacDev_ed/blog/2009/01/21/getting-boundingbox-of-svg-elements which I am using.
So: I'm creating an group of text elements like this in the 'defs' section of my SVG:
<svg id="canvas" width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="post_it">
<text x="0" y="30" id="heading" class="heading">My Heading</text>
<text x="0" y="45" id="description" class="description">This will contain the description</text>
<text x="0" y="60" id="company" class="company">Very Big Company Ltd.</text>
</g>
And I'm displaying the text with a 'use' element like this:
<use id="12345" class="postit" xlink:href="#post_it" onclick="showId(this);"/>
I'm using the onclick to trigger a call to the following javascript function (defined in 'defs' section):
function showId(elem) {
post_it_rect=getBBoxAsRectElement(elem);
document.getElementById('canvas').appendChild(post_it_rect);
}
(The 'getBBoxAsRectElement(elem)' is from the link I posted).
As this stands; this works just fine - however if I change my 'use' element to position the text in a different place like this:
<use x="100" y="100" id="12345" class="postit" xlink:href="#post_it" onclick="showId(this);"/>
Now, the text displays in the correct place, but the resultant 'background-color' (actually a 'rect' element with opacity of 0.5) still shows on the top-left of the svg canvass - and the function used to calculate the rect is returning '-2' rather than '100' ('-98'?) as I need (I think).
What do I need to do to line up the 'rect' elements and the text elements ?
The author of the (very helpful article btw) script provides a more advanced script to draw a box round any 'bb' in an SVG, but I couldn't get this to work (missing 'transform' functions?).
I'm using Firefox 7.x to render the SVG ; and I'm loading a .svg file (ie, not embedded in html etc) straight from disk to test this).
Yes, you may need to compensate yourself for the x and y attributes on the <use> element for the time being, I'll try to find some time to update the blogpost and script.
Here's a draft SVG 1.1 test that among other things checks that the effect of the x and y attributes are included in the bbox. The line starting [myUse] is the one that tests this case, if it's red then that subtest failed. Chromium and Opera Next both pass that subtest, while Firefox nightly and IE9 doesn't. Note that the test itself has not gone through full review yet, and that it may still change.

Categories