Skip to Content

Recently there was a question from Satyam Ravada asking how to show percentages on a pie chart. With the current version, there is no option to do that.

I tried to do the same thing some time back and gave up as it turn out to be a complex one (see the discussion thread for the reasons why I gave up then).

Then Dirk Mayrock’s push to find a solution motivated me to revisit the same thing. I worked on a prototype and shared a screenshot with the community here.

There were a few requests to share the design and here it is.

The basic idea (same as Dirk Mayrock’s) is have multiple textboxes with the percentage share value and place it on the pie chart at their appropriate pie sector position.

There are a few assumptions and limitations

a) only a finite and fixed number of data series

b) series dimension values are hard coded

c) legend is not enabled

To resolve limitations a) and b) SAP has to extend data-types to include Arrays and related Array methods.

Limitation c) is solvable by code adjustments, but the varying data series description lengths make it cumbersome to position texts. Instead of showing a separate legend, I show the series name on the pie chart itself.

Following is the pie-chart with data overlay.

/wp-content/uploads/2013/07/piechart_254292.jpg

and the component outline

/wp-content/uploads/2013/07/component_outline_254293.jpg

Here are the steps

a) calculate the percentage values of each product group

var varTotal = DS_1.getDataAsString("49CLRTYHGPD9YNWTY0XYOD5QV", {});
var varMOB1  = DS_1.getDataAsString("49CLRTYHGPD9YNWTY0XYOD5QV", {"0D_NW_PROD__0D_NW_PRDGP": "MOB1"});
var varMOB2  = DS_1.getDataAsString("49CLRTYHGPD9YNWTY0XYOD5QV", {"0D_NW_PROD__0D_NW_PRDGP": "MOB2"});
var varMON1  = DS_1.getDataAsString("49CLRTYHGPD9YNWTY0XYOD5QV", {"0D_NW_PROD__0D_NW_PRDGP": "MON1"});
var varMON2  = DS_1.getDataAsString("49CLRTYHGPD9YNWTY0XYOD5QV", {"0D_NW_PROD__0D_NW_PRDGP": "MON2"});
var varNB1   = DS_1.getDataAsString("49CLRTYHGPD9YNWTY0XYOD5QV", {"0D_NW_PROD__0D_NW_PRDGP": "NB1"});
var varNB2   = DS_1.getDataAsString("49CLRTYHGPD9YNWTY0XYOD5QV", {"0D_NW_PROD__0D_NW_PRDGP": "NB2"});
var fltTotal  = Convert.stringToFloatUsingLocale(varTotal);
var ratioMOB1 = Convert.stringToFloatUsingLocale(varMOB1)/fltTotal;
var ratioMOB2 = Convert.stringToFloatUsingLocale(varMOB2)/fltTotal;
var ratioMON1 = Convert.stringToFloatUsingLocale(varMON1)/fltTotal;
var ratioMON2 = Convert.stringToFloatUsingLocale(varMON2)/fltTotal;
var ratioNB1  = Convert.stringToFloatUsingLocale(varNB1)/fltTotal;
var ratioNB2  = Convert.stringToFloatUsingLocale(varNB2)/fltTotal;

b) set the text values of each text-box with the percentage value and the product group

TEXT_1.setText("MOB1 \n" + Convert.floatToString(ratioMOB1,"#0.00%"));
TEXT_2.setText("MOB2 \n" + Convert.floatToString(ratioMOB2,"#0.00%"));
TEXT_3.setText("MON1 \n" + Convert.floatToString(ratioMON1,"#0.00%"));
TEXT_4.setText("MON2 \n" + Convert.floatToString(ratioMON2,"#0.00%"));
TEXT_5.setText("NB1 \n"  + Convert.floatToString(ratioNB1,"#0.00%"));
TEXT_6.setText("NB2 \n"  + Convert.floatToString(ratioNB2,"#0.00%"));

c) evaluate the center of the pie chart

var chWidth  = CHART_1.getWidth();
var chHeight = CHART_1.getHeight();
var chTop    = CHART_1.getTopMargin();
var chLeft   = CHART_1.getLeftMargin();
if (chHeight > 0.8 * chWidth){
          chHeight = Convert.stringToInt(Convert.floatToString((chWidth * 0.80),"###0"));
}
var xCenter = chLeft + (chWidth - 52)/2 + 20;
var yCenter = chTop  + (chHeight - 76)/2 + 28;
var radius  = (chHeight - 76)/2 * 0.70;

d) position each text-box (which at design time were positioned at 0,0) radially at the center of each product group sector using sine and cosine values

var xAdjust = 30;
var yAdjust = 15;
var prevRatio = 0.0;
var thisRadian = 0.0;
var prevRadian = 0.0;
var x = 0.00;
var cosineValue = 0.00;
var sineValue = 0.00;
var pi = 3.141593;
thisRadian = pi * ratioMOB1;
prevRadian = 2 * pi * prevRatio;
x = thisRadian + prevRadian - (pi/2);
sineValue = x - x*x*x/6 + x*x*x*x*x/120 - x*x*x*x*x*x*x/5040 + x*x*x*x*x*x*x*x*x/362880 - x*x*x*x*x*x*x*x*x*x*x/39916800;
cosineValue = 1 - x*x/2 + x*x*x*x/24 - x*x*x*x*x*x/720 + x*x*x*x*x*x*x*x/40320 - x*x*x*x*x*x*x*x*x*x/3628800;
var xMOB1 = Convert.stringToInt(Convert.floatToString((xCenter + radius * cosineValue), "###0"));
var yMOB1 = Convert.stringToInt(Convert.floatToString((yCenter + radius * sineValue), "###0"));
TEXT_1.setLeftMargin(xMOB1 - xAdjust);
TEXT_1.setTopMargin(yMOB1 - yAdjust);
prevRatio = prevRatio + ratioMOB1;
thisRadian = pi * ratioMOB2;
prevRadian = 2 * pi * prevRatio;
x = thisRadian + prevRadian - (pi/2);
sineValue = x - x*x*x/6 + x*x*x*x*x/120 - x*x*x*x*x*x*x/5040 + x*x*x*x*x*x*x*x*x/362880 - x*x*x*x*x*x*x*x*x*x*x/39916800;
cosineValue = 1 - x*x/2 + x*x*x*x/24 - x*x*x*x*x*x/720 + x*x*x*x*x*x*x*x/40320 - x*x*x*x*x*x*x*x*x*x/3628800;
var xMOB2 = Convert.stringToInt(Convert.floatToString((xCenter + radius * cosineValue), "###0"));
var yMOB2 = Convert.stringToInt(Convert.floatToString((yCenter + radius * sineValue), "###0"));
TEXT_2.setLeftMargin(xMOB2 - xAdjust);
TEXT_2.setTopMargin(yMOB2 - yAdjust);
prevRatio = prevRatio + ratioMOB2;
thisRadian = pi * ratioMON1;
prevRadian = 2 * pi * prevRatio;
x = thisRadian + prevRadian - (pi/2);
sineValue = x - x*x*x/6 + x*x*x*x*x/120 - x*x*x*x*x*x*x/5040 + x*x*x*x*x*x*x*x*x/362880 - x*x*x*x*x*x*x*x*x*x*x/39916800;
cosineValue = 1 - x*x/2 + x*x*x*x/24 - x*x*x*x*x*x/720 + x*x*x*x*x*x*x*x/40320 - x*x*x*x*x*x*x*x*x*x/3628800;
var xMON1 = Convert.stringToInt(Convert.floatToString((xCenter + radius * cosineValue), "###0"));
var yMON1 = Convert.stringToInt(Convert.floatToString((yCenter + radius * sineValue), "###0"));
TEXT_3.setLeftMargin(xMON1 - xAdjust);
TEXT_3.setTopMargin(yMON1 - yAdjust);
prevRatio = prevRatio + ratioMON1;
thisRadian = pi * ratioMON2;
prevRadian = 2 * pi * prevRatio;
x = thisRadian + prevRadian - (pi/2);
sineValue = x - x*x*x/6 + x*x*x*x*x/120 - x*x*x*x*x*x*x/5040 + x*x*x*x*x*x*x*x*x/362880 - x*x*x*x*x*x*x*x*x*x*x/39916800;
cosineValue = 1 - x*x/2 + x*x*x*x/24 - x*x*x*x*x*x/720 + x*x*x*x*x*x*x*x/40320 - x*x*x*x*x*x*x*x*x*x/3628800;
var xMON2 = Convert.stringToInt(Convert.floatToString((xCenter + radius * cosineValue), "###0"));
var yMON2 = Convert.stringToInt(Convert.floatToString((yCenter + radius * sineValue), "###0"));
TEXT_4.setLeftMargin(xMON2 - xAdjust);
TEXT_4.setTopMargin(yMON2 - yAdjust);
prevRatio = prevRatio + ratioMON2;
thisRadian = pi * ratioNB1;
prevRadian = 2 * pi * prevRatio;
x = thisRadian + prevRadian - (pi/2);
sineValue = x - x*x*x/6 + x*x*x*x*x/120 - x*x*x*x*x*x*x/5040 + x*x*x*x*x*x*x*x*x/362880 - x*x*x*x*x*x*x*x*x*x*x/39916800;
cosineValue = 1 - x*x/2 + x*x*x*x/24 - x*x*x*x*x*x/720 + x*x*x*x*x*x*x*x/40320 - x*x*x*x*x*x*x*x*x*x/3628800;
var xNB1 = Convert.stringToInt(Convert.floatToString((xCenter + radius * cosineValue), "###0"));
var yNB1 = Convert.stringToInt(Convert.floatToString((yCenter + radius * sineValue), "###0"));
TEXT_5.setLeftMargin(xNB1 - xAdjust);
TEXT_5.setTopMargin(yNB1 - yAdjust);
prevRatio = prevRatio + ratioNB1;
thisRadian = pi * ratioNB2;
prevRadian = 2 * pi * prevRatio;
x = thisRadian + prevRadian - (pi/2);
sineValue = x - x*x*x/6 + x*x*x*x*x/120 - x*x*x*x*x*x*x/5040 + x*x*x*x*x*x*x*x*x/362880 - x*x*x*x*x*x*x*x*x*x*x/39916800;
cosineValue = 1 - x*x/2 + x*x*x*x/24 - x*x*x*x*x*x/720 + x*x*x*x*x*x*x*x/40320 - x*x*x*x*x*x*x*x*x*x/3628800;
var xNB2 = Convert.stringToInt(Convert.floatToString((xCenter + radius * cosineValue), "###0"));
var yNB2 = Convert.stringToInt(Convert.floatToString((yCenter + radius * sineValue), "###0"));
TEXT_6.setLeftMargin(xNB2 - xAdjust);
TEXT_6.setTopMargin(yNB2 - yAdjust);

Since SAP did not provide any trigonometric functions, I have to calculate the sine and cosine values using the infinite series algorithm, but to a finite level only. There are also logic to determine the radian value for the trig functions. This way each text-box is placed at the center of the sector.

The above script works fine for any size of the pie-chart and displays the data points.

Due to the limitations mentioned above as well as hard-coded dimension values, it can be only implemented if the series is known in advance and doesn’t change.

SDKs and the upcoming SPs will definitely provide a cleaner and simpler option, but until then, the above information will serve your need if you want something similar.

Thanks.

To report this post you need to login first.

6 Comments

You must be Logged on to comment or reply to a post.

  1. Dirk Mayrock

    Hi,

    Ha ha… You are Crazy! I Realy like that very much!!!

    I LIKE!!!

    And now I know why I failed on bringing my idea to script.

    Respect!

    Dirk

    (0) 

Leave a Reply