We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation .
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement . We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
For some big numbers in pair with rather small ones, labels are overlapping other as there's no sufficient space for all of them.
Here is the demo: http://jsfiddle.net/qzk61f5h/
And this is how it looks like:
I think that library should handle this case properly - maybe by showing them stacked up at the top of another?
The text was updated successfully, but these errors were encountered:
Same issue here
Sorry, something went wrong.
Is there any progress on fixing this issue?
Seems like a pretty big bug.
I have also run into this issue and I'm trying to find a way around it, but with no success.
Very big issue for me using this library.
Would be nice to have an automated solution, but in the mean time I worked around by using Legend instead and had coloured pie slices, added some paddingAngle to make small slices obvious and added minAngle to also make the slice visible. You an tweak the angles, but it gives an idea.
Legend
paddingAngle
minAngle
Not bothering to create jsfiddle because they always seem broken by the time I view ... the relevant code snippets below ...
// Static data, or you could have a list of available colours and map your data to contain different fill colours const myData = [ { name: 'Name A', value: 9999, fill: '#908ce2', }, { name: 'Name B', value: 5000, fill: '#2fe18a', }, { name: 'Name C', value: 2, fill: '#ef8a88', }, { name: 'Name D', value: 1, fill: '#8fcddd', }, ]; ... <ResponsiveContainer width="60%" minHeight={400}> <PieChart> <Pie data={myData} dataKey="value" nameKey="name" fill="#8884d8" legendType="circle" paddingAngle={1} minAngle={1} /> <Tooltip/> <Legend/> </PieChart> </ResponsiveContainer>
Has anyone been able find a workaround to this problem?
I have a problem with this too. I tried to use custom label, but i can't create algorithm to calc positions for labels...=(
Highcharts handle this smoothly... It should be great to have a workaround for this issue. https://www.highcharts.com/demo/pie-basic
I'm having the same problem.
Does anyone have a solution?
I also had issues placing custom label text, but I am using the following solution for that as well as some logic for translating the labels because the built in locations were not working:
const offsetLabel = (name, x, y) => { let offsetX let offsetY if (name === '100%') return [22, 5] if (x <= 145) { offsetX = 14 } else { offsetX = 22 } if (y <= 105) { offsetY = 11 } else { offsetY = 5 } return [offsetX, offsetY] } const renderLabelContent = props => { const { name, x, y, midAngle } = props const [offsetX, offsetY] = offsetLabel(name, x, y) return ( <g transform={`translate(${x}, ${y})`} textAnchor={(midAngle < -90 || midAngle >= 90) ? 'end' : 'start'}> <text x={offsetX} y={offsetY}>{name}</text> </g> ) }
I am passing the function renderLabelContent to the attribute label below:
<ReactPieChart width={200} height={220}> <Pie startAngle={90} endAngle={450} innerRadius={innerRadius || 0} data={data} paddingAngle={paddingAngle} stroke={stroke} label={renderLabelContent} labelLine={false} dataKey='value' /> </ReactPieChart>
We ended up creating a custom label function that just returns NULL for values that are less than a configured percent of the total. Since the value is shown in a tooltip on hover, this was the cleanest solution we could think of without trying to calculate positions. What also helped was being sure to sort the values such that the smallest always appear at "3 'o clock" to minimize the possibility of horizontal overlap.
That being said, this is certainly not ideal and would love to know if there's actual interest in getting a real fix for the library.
+1 for the issue, any update?
Thanks to @zgallagher08 initial comment, I came with following code, which pretty much provides the same output as @rmkpatchaa shown :
renderCustomizedLabel({ cx, cy, midAngle, innerRadius, outerRadius, value, color, startAngle, endAngle}) { const RADIAN = Math.PI / 180; const diffAngle = endAngle - startAngle; const delta = ((360-diffAngle)/15)-1; const radius = innerRadius + (outerRadius - innerRadius); const x = cx + (radius+delta) * Math.cos(-midAngle * RADIAN); const y = cy + (radius+(delta*delta)) * Math.sin(-midAngle * RADIAN); return ( <text x={x} y={y} fill={color} textAnchor={x > cx ? 'start' : 'end'} dominantBaseline="central" fontWeight="normal"> {value} </text> ); }; renderCustomizedLabelLine(props){ let { cx, cy, midAngle, innerRadius, outerRadius, color, startAngle, endAngle } = props; const RADIAN = Math.PI / 180; const diffAngle = endAngle - startAngle; const radius = innerRadius + (outerRadius - innerRadius); let path=''; for(let i=0;i<((360-diffAngle)/15);i++){ path += `${(cx + (radius+i) * Math.cos(-midAngle * RADIAN))},${(cy + (radius+i*i) * Math.sin(-midAngle * RADIAN))} ` } return ( <polyline points={path} stroke={color} fill="none" /> ); }
Any update? Currently can't use this lib with this bug and @abishek28007 solution doesn't render correctly for us.
@abishek28007 After doing some changes to your customized label function it's working fine for me. I'm a bit confused about where to set the customized line function to the pie chart ??
@senelithperera
<Pie labelLine={renderCustomizedLabelLine} label={renderCustomizedLabel} >
@abishek28007 Thanks for getting back to me, but unfortunately your solution didn't fix my problem 100% so I moved to temporary solution, which is showing the labels like this (custom active Index) , as specified in this
We need a concrete solution for showing labels in pie charts without having the overlapping issue !!
@senelithperera , can you post the modification that you made in my code, would help me to come up with a fix for this.
Any update on this? It's a pretty serious issue for quite an ordinary and common use case and hasn't been solved for a while now. Is it being worked on?
<Pie isAnimationActive={false} isUpdateAnimationActive={true} data={props.data} dataKey="value" nameKey="name" labelLine={({ cx, cy, midAngle, innerRadius, outerRadius, value, index }) => { const RADIAN = Math.PI / 180;
// eslint-disable-next-line let radius1 = 20 + innerRadius + (outerRadius - innerRadius); let radius2 = innerRadius + (outerRadius - innerRadius); // eslint-disable-next-line let x2 = cx + radius1 * Math.cos(-midAngle * RADIAN); // eslint-disable-next-line let y2 = cy + radius1 * Math.sin(-midAngle * RADIAN); let x1 = cx + radius2 * Math.cos(-midAngle * RADIAN); // eslint-disable-next-line let y1 = cy + radius2 * Math.sin(-midAngle * RADIAN); if (value<30){ return null; } return( <line x1={x1} y1={y1} x2={x2} y2={y2} stroke="#ccc" strokeWidth={1}> </line> ) }} label={({ cx, cy, midAngle, innerRadius, outerRadius, value, index }) => { const RADIAN = Math.PI / 180; // eslint-disable-next-line let radius = 20 + innerRadius + (outerRadius - innerRadius); // eslint-disable-next-line let x = cx + radius * Math.cos(-midAngle * RADIAN); // eslint-disable-next-line let y = cy + radius * Math.sin(-midAngle * RADIAN); if (value<30){ return null; } return ( <text x={x} y={y} fill="#000" fontWeight="300" fontSize="13px" fontFamily="'Source Sans Pro', 'Roboto', 'Helvetica Neue', 'Helvetica', 'Arial', 'sans-serif'" textAnchor={x > cx ? "start" : "end"} dominantBaseline="central" > {props.data[index].name} {value} </text> ); }} cx="50%" cy="50%" outerRadius="84%" innerRadius="40%" fill="#8884d8" > {/* <LabelList dataKey="name" position="outside" /> */} <Label value={100} position="center" fontSize={24} fontWeight={400}></Label> </Pie>
Use This <Pie isAnimationActive={false} isUpdateAnimationActive={true} data={props.data} dataKey="value" nameKey="name" labelLine={({ cx, cy, midAngle, innerRadius, outerRadius, value, index }) => { const RADIAN = Math.PI / 180;
@avinashsingh953 I assume your solution won't render label for value < 30
Yes But you can adjust the value or calculate the angle for which labels can be hidden
@abishek28007 - what is the significance or reason for the number 15 in working out the delta variable in your solution? Thanks
@lxm7 15 and -1 are some arbitrary value which worked my use case, depending upon the use case they may change. I am trying to generalise it, any suggestion is appreciated.
I think this not only affects pie charts but also stacked bar charts and line charts.
I’d like to bump this issue. Any solid solution would be appreciated
Hi. We are facing the same issue as well where the labels are getting overlapped. Any fix for this? Thanks.
We've been looking for a solution for some time; even considered implementing something ourselves, possibly using things like d3-labeler . All for naught; we finally changed the UI and implemented the information via a legend. Seems to me like the only reasonable solution at this point.
any update on this?
Automatically placing an arbitrary amount of labels for arbitrarily thin slices of a Pie is not reasonable.
Instead we could extend the label feature:
Seems reasonable, @nikolasrieble ... and maybe a combination of the two: show the labels that can fit, and then add hover for the others. Because for our use case, we definitely want the users to see the percentage. Using a legend as we did turned out to work great: see it all at a glance, no matter how small.
Our workaround:
const chartCountryAllocations = [ { value : 0.6454 , name : 'United States' } , { value : 0.0444 , name : 'Germany' } , { value : 0.0438 , name : 'Japan' } , { value : 0.0412 , name : 'Switzerland' } , { value : 0.0406 , name : 'France' } , { value : 0.0394 , name : 'Netherlands' } , { value : 0.0357 , name : 'China' } , { value : 0.0266 , name : 'Canada' } , { value : 0.0251 , name : 'Denmark' } , { value : 0.0354 , name : 'Others' } , { value : 0.0224 , name : 'Cash' } , ] const renderCustomizedLabel = ( { cx , cy , midAngle , outerRadius , percent , name , fontSize , index } ) => { const RADIAN = Math . PI / 180 const sin = Math . sin ( RADIAN * midAngle ) const cos = Math . cos ( RADIAN * midAngle ) const startX = cx + ( outerRadius ) * cos const startY = cy + ( outerRadius ) * - sin const middleY = cy + ( outerRadius + 50 * Math . abs ( sin ) ) * - sin let endX = startX + ( cos >= 0 ? 1 : - 1 ) * 90 let textAnchor = cos >= 0 ? 'start' : 'end' const mirrorNeeded = midAngle > - 270 && midAngle < - 210 && percent < 0.04 && index % 2 === 1 if ( mirrorNeeded ) { endX = startX + outerRadius * - cos * 2 + 100 textAnchor = 'start' } return ( < g > < path d = { `M${ startX } ,${ startY } L${ startX } ,${ middleY } L${ endX } ,${ middleY } ` } fill = "none" stroke = '#000' strokeWidth = { 3 } / > < text x = { endX + ( cos >= 0 || mirrorNeeded ? 1 : - 1 ) * 12 } y = { middleY + fontSize / 2 } textAnchor = { textAnchor } fontSize = { fontSize } > { ` ${ name || '' } ${ ( percent * 100 ) . toFixed ( 2 ) } %` } < / text> < / g > ) } < div style = { { width : '1000px' , height : '575px' } } > < ResponsiveContainer width = "100%" height = "100%" > < PieChart width = { 400 } height = { 400 } > < Pie data = { chartCountryAllocations } cx = '50%' cy = '50%' startAngle = { 90 } endAngle = { - 270 } labelLine = { false } paddingAngle = { 5 } dataKey = "value" fontSize = { 30 } isAnimationActive = { false } label = { renderCustomizedLabel } > { chartCountryAllocations . map ( ( allo , idx ) => ( < Cell key = { idx } fill = '#51A760' / > ) ) } < / Pie > < / PieChart > < / ResponsiveContainer > < / div >
For some big numbers in pair with rather small ones, labels are overlapping other as there's no sufficient space for all of them. Here is the demo: http://jsfiddle.net/qzk61f5h/ And this is how it looks like: I think that library should handle this case properly - maybe by showing them stacked up at the top of another?
Ans: Just we should minAngle to the pie component likely it quite gave the expected result. Just try like this.
You can change the min angle and change the fontSize and manage a custom label with custom variables so that the text scrolls to your liking
Thanks to @zgallagher08 initial comment, I came with following code, which pretty much provides the same output as @rmkpatchaa shown : renderCustomizedLabel({ cx, cy, midAngle, innerRadius, outerRadius, value, color, startAngle, endAngle}) { const RADIAN = Math.PI / 180; const diffAngle = endAngle - startAngle; const delta = ((360-diffAngle)/15)-1; const radius = innerRadius + (outerRadius - innerRadius); const x = cx + (radius+delta) * Math.cos(-midAngle * RADIAN); const y = cy + (radius+(delta*delta)) * Math.sin(-midAngle * RADIAN); return ( <text x={x} y={y} fill={color} textAnchor={x > cx ? 'start' : 'end'} dominantBaseline="central" fontWeight="normal"> {value} </text> ); }; renderCustomizedLabelLine(props){ let { cx, cy, midAngle, innerRadius, outerRadius, color, startAngle, endAngle } = props; const RADIAN = Math.PI / 180; const diffAngle = endAngle - startAngle; const radius = innerRadius + (outerRadius - innerRadius); let path=''; for(let i=0;i<((360-diffAngle)/15);i++){ path += `${(cx + (radius+i) * Math.cos(-midAngle * RADIAN))},${(cy + (radius+i*i) * Math.sin(-midAngle * RADIAN))} ` } return ( <polyline points={path} stroke={color} fill="none" /> ); }
this doesn't work
the result is something like....
anyone knows how to handle this in line charts?
@lucaspieran no way to handle it automatically, but you can use a combo of position and offset props on LabelList
position
offset
LabelList
Refer this example for better solution:
const renderCustomizedLabel = (props) => { const RADIAN = Math.PI / 180; const { cx, cy, midAngle, outerRadius, fill, percent, value, name, index } = props; const sin = Math.sin(-RADIAN * midAngle); const cos = Math.cos(-RADIAN * midAngle); const sx = cx + outerRadius * cos; const sy = cy + outerRadius * sin; const mx = cx + (outerRadius + 20) * cos; const my = cy + (outerRadius + 20) * sin; const isLeftSide = midAngle < 90 && midAngle > -90; // Change 10 to half of the number of items per side const ySpacing = 15; // Adjust the vertical spacing between lines const xSpacing = -100; // Adjust the horizontal spacing between left and right sides const topSpacing = 50; const ey = !isLeftSide ? topSpacing + (data?.length - index) * ySpacing : topSpacing + (index + 1) * ySpacing; const textAnchor = isLeftSide ? "start" : "end"; const xPosition = cx + (isLeftSide ? -1 : 1) * xSpacing; let keyName = name.split(" "); return ( <g> <path d={`M${sx},${sy}L${mx},${my}L${xPosition},${ey}`} stroke={fill} fill="none" /> <circle cx={xPosition} cy={ey} r={2} fill={fill} stroke="none" /> <text x={textAnchor === "start" ? xPosition + 10 : xPosition - 10} y={ey + 5} fontSize={15} textAnchor={textAnchor} fill={fill} > {`${keyName}: ${value} `} </text> </g> ); }; <PieChart width={500} height={500}> <Pie data={data} cx={200} cy={200} labelLine={false} label={renderCustomizedLabel} outerRadius={60} fill="#8884d8" dataKey="value" startAngle={90} endAngle={-270} isAnimationActive={false} > {data.map((entry, index) => ( <Cell key={`cell-${index}`} fill={COLORS[index % COLORS.length]} /> ))} </Pie> </PieChart>
No branches or pull requests