Question
Pegasystems
NL
Last activity: 18 Jul 2017 10:16 EDT
Bullet graph
Hello All,
Recent I got the question if we support high effective informative graphical gadgets. Bullet graph - Wikipedia, the free encyclopedia
see link for more info. Can someone advice me in this?
Thanks, Ander
Hello All,
Recent I got the question if we support high effective informative graphical gadgets. Bullet graph - Wikipedia, the free encyclopedia
see link for more info. Can someone advice me in this?
Thanks, Ander
A bullet graph is a variation of a bar graph developed by Stephen Few. Seemingly inspired by the traditional thermometer charts and progress bars found in many dashboards, the bullet graph serves as a replacement for dashboard gauges and meters. Bullet graphs were developed to overcome the fundamental issues of gauges and meters: they typically display too little information, require too much space, and are cluttered with useless and distracting decoration. The bullet graph features a single, primary measure (for example, current year-to-date revenue), compares that measure to one or more other measures to enrich its meaning (for example, compared to a target), and displays it in the context of qualitative ranges of performance, such as poor, satisfactory, and good. The qualitative ranges are displayed as varying intensities of a single hue to make them discernible by those who are color blind and to restrict the use of colors on the dashboard to a minimum.
Below is an example of a single bullet graph:
Below is the same example, this time with labels to identify each part of the bullet graph.
Bullet graphs may be horizontal or vertical, and may be stacked to allow comparisons of several measures at once
***Updated by moderator: Lochan to close post***
This post has been archived for educational purposes. Contents and links will no longer be updated. If you have the same/similar question, please write a new post.
-
Likes (1)
Tanul Thanvi -
Share this page Facebook Twitter LinkedIn Email Copying... Copied!
Accepted Solution
<pega:choose>
<pega:when test="pxRequestor.pyPegaDesignMode != 'true'">
<%--Include the chart JS files--%>
<pega:onlyonce name="pzCKChartScripts">
<pega:static app="webwb" type="script">
<pega:bundle name="pzpega_chart_scripts" />
</pega:static>
</pega:onlyonce>
<%--Generate an unique id for the chart--%>
<%
String uniqueId = "CUSTOMCHART"+Long.toString(((com.pega.pegarules.priv.context.PegaRequestor) tools.getRequestor()).currentTimeUnique());
double value = 4386, maxValue = 7500;
try {
value = Double.valueOf(tools.getParamValue("Value"));
} catch (Exception e) { value=0; }
try {
maxValue = Double.valueOf(tools.getParamValue("MaxValue"));
} catch (Exception e) { maxValue=0; }
if(value == 0 || maxValue == 0) {
if(value == 0 && maxValue != 0)
value = 1;
else if(value != 0 && maxValue == 0)
maxValue = value*1.25;
else {
value = 75;
maxValue = 100;
}
<pega:choose>
<pega:when test="pxRequestor.pyPegaDesignMode != 'true'">
<%--Include the chart JS files--%>
<pega:onlyonce name="pzCKChartScripts">
<pega:static app="webwb" type="script">
<pega:bundle name="pzpega_chart_scripts" />
</pega:static>
</pega:onlyonce>
<%--Generate an unique id for the chart--%>
<%
String uniqueId = "CUSTOMCHART"+Long.toString(((com.pega.pegarules.priv.context.PegaRequestor) tools.getRequestor()).currentTimeUnique());
double value = 4386, maxValue = 7500;
try {
value = Double.valueOf(tools.getParamValue("Value"));
} catch (Exception e) { value=0; }
try {
maxValue = Double.valueOf(tools.getParamValue("MaxValue"));
} catch (Exception e) { maxValue=0; }
if(value == 0 || maxValue == 0) {
if(value == 0 && maxValue != 0)
value = 1;
else if(value != 0 && maxValue == 0)
maxValue = value*1.25;
else {
value = 75;
maxValue = 100;
}
}
if(maxValue <= value) {
maxValue = value+1;
}
String sImage = tools.getParamValue("ImageType");
String sImageURL = "webwb/activity-icon-steps.svg";
String sColor = "32AFFF";
int iXAdjust = 45, iYAdjust = 45;
if(("steps").equalsIgnoreCase(sImage)) {
sImageURL = sImageURL = "webwb/activity-icon-steps.svg";
sColor = "32AFFF";
}
else if(("calories").equalsIgnoreCase(sImage)) {
sImageURL = sImageURL = "webwb/activity-icon-calories.svg";
sColor = "ED4036";
iXAdjust = 43;
}
else if(("heart").equalsIgnoreCase(sImage)) {
sImageURL = sImageURL = "webwb/activity-icon-heart.svg";
sColor = "1FCE6D";
iXAdjust = 45;
iYAdjust = 40;
}
else if(("sleep").equalsIgnoreCase(sImage)) {
sImageURL = sImageURL = "webwb/activity-icon-sleep.svg";
sColor = "A052C4";
iXAdjust = 43;
iYAdjust = 44;
}
%>
<%--Generate div that will hold the chart--%>
<div data-ctl="["Chart"]" id="<%=uniqueId%>" style="width:<p:r n='Param.width'/>;height:<p:r n='Param.height'/>" data-loadmode="auto" data-chartsubtype="dial" data-charttype="gauge" data-library="fusion"></div>
<%--Initialize the chart using pxChart's initializer--%>
<script>
Charts.initSingleChart('<%=uniqueId%>',{sourcedata:{
"chart": {
"lowerLimit": "0",
"upperLimit": "<%=maxValue %>",
"showGaugeBorder": "0",
"canvasBGAlpha":"0",
"bgAlpha":"0",
"showValue": "0",
"valueBelowPivot": "1",
"showCanvasBorder":"0",
"showBorder":"0",
"usePlotGradientColor": "0",
"showTickMarks":"0",
"showTickValues":"0",
"gaugeStartAngle":"90",
"gaugeEndAngle":"-270",
"gaugeFillRatio":"0",
"gaugeFillMix":"{light-0}",
"showshadow":"0",
"pivotFillAlpha":"0",
"majorTMNumber":"0",
"minorTMNumber":"0",
"majorTMAlpha":"0",
"minorTMAlpha":"0",
"majorTMColor":"#D9DAD8",
"gaugeInnerRadius":"82%",
"chartLeftMargin":"0",
"chartRightMargin":"0",
"chartTopMargin":"0",
"chartBottomMargin":"0",
"canvasPadding":"0",
"showToolTip":"0",
"baseFont":"OpenSans"
},
"colorRange": {
"color": [
{
"minValue": "0",
"maxValue": "<%=value %>",
"code": "#<%=sColor %>"
},
{
"minValue":"<%=value+1 %>",
"maxValue": "<%=maxValue %>",
"code": "#D9DAD8"
}
]
},
"dials": {
"dial": [{
"value": "0",
"borderAlpha":"0",
"baseWidth":"0",
"topWidth":"0"
}]
},
"annotations": {
"origW":"130",
"origH":"130",
"autoscale":"1",
"scaleImages":"1",
"scaleText":"1",
"groups": [{
"items": [
{
"id":"icon",
"type":"image",
"url":"<%=sImageURL %>",
"x":"$gaugeCenterX-<%=iXAdjust %>",
"y":"$gaugeCenterY-<%=iYAdjust %>",
"xScale":"220",
"yScale":"220"
}
]
}]
}
}});
</script>
</pega:when>
<pega:otherwise>
Gauge chart, Static score out of 100
</pega:otherwise>
</pega:choose>
Updated: 17 Jun 2015 5:41 EDT
Pegasystems Inc.
GB
Hi Ander,
I'm not sure that we have any 'Bullet Graph' charts built-into PRPC (somebody else may know though....)
However: you can include 3rd party JS/CSS/HTML into PRPC quite easily to do this.
I have mocked-up a very simple test showing this here:
I started with this example here: bl.ocks.org - about
Which contains three files:
index.htm
bullet.js
bullets.json
And an include to another JS library (d3) : Tutorials · mbostock/d3 Wiki · GitHub
This draws a fairly nice 'Bullet Chart' based on the JSON data input like this:
So how to use this from PRPC ?
This is what I did as a proof-of-concept:
1. Create a new "Rule-File-Text" (Create | Technical | Text File) rule
Hi Ander,
I'm not sure that we have any 'Bullet Graph' charts built-into PRPC (somebody else may know though....)
However: you can include 3rd party JS/CSS/HTML into PRPC quite easily to do this.
I have mocked-up a very simple test showing this here:
I started with this example here: bl.ocks.org - about
Which contains three files:
index.htm
bullet.js
bullets.json
And an include to another JS library (d3) : Tutorials · mbostock/d3 Wiki · GitHub
This draws a fairly nice 'Bullet Chart' based on the JSON data input like this:
So how to use this from PRPC ?
This is what I did as a proof-of-concept:
1. Create a new "Rule-File-Text" (Create | Technical | Text File) rule
Set the Label to 'bullet', 'App Name' to 'webwb' and File Type(Extension) to 'js'):
Paste the contents of the 'bullet.js' into this file - no modifications are needed , save the rule.
2. Create a new "Rule-Obj-HTML" ((Create | Technical | HTML File) rule
It doesn't really matter so much what you call this - I called it 'Bullet'.
Paste the contents of the 'index.html' into the "HTML Source".
We need to make a couple of changes here:
Change the reference to 'bullets.js' to get it as 'static content' from PRPC (the rule we previously saved in step 1):
So locate the reference to this 'bullet.js' and comment it out - replace with the 'pega-tag' like this:
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> <!-- <script src="bullet.js"></script> --> <pega:static type="script" app="webwb" > <pega:file name="Bullet.js" /> </pega:static>
The original example performs an AJAX request for the file 'bullets.json' from the server - it was easier to get this working by cheating slightly here - removing the AJAX call , and just hardcoding the JSON string into the JS itself.
So I commented out the 'd3' callback function (d3.json) like this : note since we are now getting rid of the calling 'wrapping' function here - we need to remove the closing '})' at the end.
So this is the abridged changes made - I hope this is clear here:
/* d3.json("bullets.json", function(error, data) { if (error) throw error; */ data=[ {"title":"Revenue","subtitle":"US$, in thousands","ranges":[150,225,300],"measures":[220,270],"markers":[250]}, {"title":"Profit","subtitle":"%","ranges":[20,25,30],"measures":[21,23],"markers":[26]}, {"title":"Order Size","subtitle":"US$, average","ranges":[350,500,600],"measures":[100,320],"markers":[550]}, {"title":"New Customers","subtitle":"count","ranges":[1400,2000,2500],"measures":[1000,1650],"markers":[2100]}, {"title":"Satisfaction","subtitle":"out of 5","ranges":[3.5,4.25,5],"measures":[3.2,4.7],"markers":[4.4]} ]; // Add the JSON data in directly [...] d3.selectAll("button").on("click", function() { svg.datum(randomize).call(chart.duration(1000)); // TODO automatic transition }); //});
Save the rule.
3. Test the HTM.
Go back to the HTML rule you created - and use 'Actions | Preview' to view the HTML rule:
This should now launch a popup window showing the Bullet Chart :
You can also test this by creating a 1-3 line Activity that does a 'New Page', 'Show-Stream', 'Page-Remove'.
SUGGESTIONS FOR IMPROVEMENTS.
1. The script still includes an external reference to 'd3' : in fact this script itself could be imported into PRPC to remove this external requirement.
In fact, it *may* be possible that you can switch out the 'd3' to use JQuery (which is shipped with PRPC OOTB) instead......
2. PRPC can provide dynamic JSON over AJAX/REST - this could be used instead of the hard-coded data here....
Or: You could just use PegaTags to generate the JSON string within the rule itself maybe...(not as nice probably - not separating out data from view....)
3. Including this code in a 'Rule-File-HTM' is probably not that useful - maybe it would be better enclosed in a 'Fragment' or a 'Control'...so that it can be more easily re-used.
Pegasystems
NL
Hello John,
Thank you very much for this!
It's a first and very useful step to go and with your detailed instructions, I'm sure I can make something out of this.
I need to seek some help to get it connected to the (real time) data I need in the dashboard of the demo; I'm not a born geek, and probably will never become one ;-(.
The data now is brought up as a mix of data pages and report definitions, so this needs attention.
When I get progress in this, I'll report back.
Kind regards, Ander
Pegasystems
IN
I think the custom chart control can support all fusionchart controls. John, you might want to refer to this internal document to help out the customer - https://mesh.pega.com/docs/DOC-27881. Note that bullet charts are supported by fusioncharts which is the charting technology used with report definitions.
Pegasystems Inc.
GB
@Rajiv Nistala - thanks for this.
I'll take a look at the doc and post relevant bits here - I can see from the Fusion Charts website that Bullet Charts are available:
Pegasystems
NL
Hello Rajiv,
this is great, and yes I see that the bullet graphs are there.
The document of Marty is very descriptive and I found already all the resources needed.
I'll have to wait to Friday to test this on P7.1.7, I'll keep you posted.
Thanks, Ander
Updated: 17 Jun 2015 9:46 EDT
Pegasystems Inc.
GB
So in summary:
7.1.8 defines implementations for the following Fusion Chart Types currently:
...But not currently the "Bullet Graph" - so it looks like you would need to use the 'Custom Chart Control' to achieve this.
The document provided by Rajiv states "However, we are able to generate any chart type Fusion supports (including maps, combo charts, sparklines[...using...] the 'pzCustomFusionChart'[...]".
It goes onto to explain how to find the XML needed for each type of chart.
For "Bullet Graphs" - the category is 'widgets' : so it appears here : http://docs.fusioncharts.com/widgets/
And then we just need to select (on the left-hand side) the 'XML Sheet' for "Bullet Graphs" - there are two in fact - vertical and horizontal.
Picking Horizontal for example shows that we need the following "XML Sheet" for our purposes:
So in summary:
7.1.8 defines implementations for the following Fusion Chart Types currently:
...But not currently the "Bullet Graph" - so it looks like you would need to use the 'Custom Chart Control' to achieve this.
The document provided by Rajiv states "However, we are able to generate any chart type Fusion supports (including maps, combo charts, sparklines[...using...] the 'pzCustomFusionChart'[...]".
It goes onto to explain how to find the XML needed for each type of chart.
For "Bullet Graphs" - the category is 'widgets' : so it appears here : http://docs.fusioncharts.com/widgets/
And then we just need to select (on the left-hand side) the 'XML Sheet' for "Bullet Graphs" - there are two in fact - vertical and horizontal.
Picking Horizontal for example shows that we need the following "XML Sheet" for our purposes:
<chart lowerLimit="0" upperLimit="100" caption="Revenue" subcaption="US $ (1,000s)" numberPrefix="$" numberSuffix="K" showValue="1"> <colorRange> <color minValue="0" maxValue="50" code="A6A6A6"/> <color minValue="50" maxValue="75" code="CCCCCC"/> <color minValue="75" maxValue="100" code="E1E1E1"/> </colorRange> <value>78.9</value> <target>80</target> </chart>
I'm not entirely sure where this XML should be saved in PRPC at this point - but I'll look into it. [see below - looks like the Fusion Charts have been updated since that document was created - and things may have changed a bit - also OP has contacted SMEs about options here as well - let's see how that pans out first].
The document also references the need to import a RAP file : but I believe (since this was written for 62SP2) - this may not be necessary anymore - the 'pzCustomFusionChart' control appears to be already shipped with PRPC 7.1.8 (and probably older versions).
[UPDATE: the RAP file also contains an example - so that will probably answer my question above, about where the Fusion "XML Sheet" should go..]
[UPDATE #2 : Actually the Fusion Site Link above is now old - the Bullet Graph on there appears to be rendered in FLASH, not HTML Canvas/JS : the newer site is: FusionCharts Developer Center I believe].
You might also want to consider logging an ENH/Feedback Request - to see if it would be feasible to include support for the Bullet Graphs within the Report Definition Chart Editor for future releases of PRPC.
Cheers
John
7.1.8 Current Fusion Charts available OOTB.
Pie
Pie - Normal
Pie - Exploded
Pie - Doughnut
Area
Area - Overlaid
Area - Stacked
Area - 100%
Column
Column - Clustered
Column - Stacked
Column - 100%
Line
Line - Curved
Line - Segment
Line - Step
Line - Reverse step
Line - Horizontal
Bar
Bar - Clustered
Bar - Stacked
Bar - 100%
Gauge
Gauge - Half dial
Gauge - Half arch
Gauge - 270 degree arch
Gauge - Speedometer
Pegasystems
NL
Hello John,
Yes you are right.
I'm asking Greg Loucas for an P7 update on the pzCustomFusionChart control that allows you to use non supported Fusion graphs in Pega.
I'm waiting for this.
Kind regards, Ander
Hi Ander,
Currently we do not support Bullet graphs OOTB with the new autogen chart control. As mentioned in previous replies within this thread, you can access any of the fusion charts with pzCustomFusion chart control, which allows you to pass XML to read in Pega data. There is also another method, which involves creating a custom control, and within that referencing JSON based on fusion's guidelines and connecting that with custom data (we actually did this for the charts presented at the Dashboard and Analytics booth at Pegaworld -- It's not entirely custom, since we're using Fusion's API, just not 100% autogen).
My recommendation would be to go here: http://www.fusioncharts.com/javascript-chart-fiddles/?type=Bullet--Chart and configure a bullet graph to your liking via jsfiddle. When you're satisfied with your results, bring it back into Pega via a custom control, set up the properties you need to hook up as parameters, then the control becomes reusable.
Accepted Solution
<pega:choose>
<pega:when test="pxRequestor.pyPegaDesignMode != 'true'">
<%--Include the chart JS files--%>
<pega:onlyonce name="pzCKChartScripts">
<pega:static app="webwb" type="script">
<pega:bundle name="pzpega_chart_scripts" />
</pega:static>
</pega:onlyonce>
<%--Generate an unique id for the chart--%>
<%
String uniqueId = "CUSTOMCHART"+Long.toString(((com.pega.pegarules.priv.context.PegaRequestor) tools.getRequestor()).currentTimeUnique());
double value = 4386, maxValue = 7500;
try {
value = Double.valueOf(tools.getParamValue("Value"));
} catch (Exception e) { value=0; }
try {
maxValue = Double.valueOf(tools.getParamValue("MaxValue"));
} catch (Exception e) { maxValue=0; }
if(value == 0 || maxValue == 0) {
if(value == 0 && maxValue != 0)
value = 1;
else if(value != 0 && maxValue == 0)
maxValue = value*1.25;
else {
value = 75;
maxValue = 100;
}
<pega:choose>
<pega:when test="pxRequestor.pyPegaDesignMode != 'true'">
<%--Include the chart JS files--%>
<pega:onlyonce name="pzCKChartScripts">
<pega:static app="webwb" type="script">
<pega:bundle name="pzpega_chart_scripts" />
</pega:static>
</pega:onlyonce>
<%--Generate an unique id for the chart--%>
<%
String uniqueId = "CUSTOMCHART"+Long.toString(((com.pega.pegarules.priv.context.PegaRequestor) tools.getRequestor()).currentTimeUnique());
double value = 4386, maxValue = 7500;
try {
value = Double.valueOf(tools.getParamValue("Value"));
} catch (Exception e) { value=0; }
try {
maxValue = Double.valueOf(tools.getParamValue("MaxValue"));
} catch (Exception e) { maxValue=0; }
if(value == 0 || maxValue == 0) {
if(value == 0 && maxValue != 0)
value = 1;
else if(value != 0 && maxValue == 0)
maxValue = value*1.25;
else {
value = 75;
maxValue = 100;
}
}
if(maxValue <= value) {
maxValue = value+1;
}
String sImage = tools.getParamValue("ImageType");
String sImageURL = "webwb/activity-icon-steps.svg";
String sColor = "32AFFF";
int iXAdjust = 45, iYAdjust = 45;
if(("steps").equalsIgnoreCase(sImage)) {
sImageURL = sImageURL = "webwb/activity-icon-steps.svg";
sColor = "32AFFF";
}
else if(("calories").equalsIgnoreCase(sImage)) {
sImageURL = sImageURL = "webwb/activity-icon-calories.svg";
sColor = "ED4036";
iXAdjust = 43;
}
else if(("heart").equalsIgnoreCase(sImage)) {
sImageURL = sImageURL = "webwb/activity-icon-heart.svg";
sColor = "1FCE6D";
iXAdjust = 45;
iYAdjust = 40;
}
else if(("sleep").equalsIgnoreCase(sImage)) {
sImageURL = sImageURL = "webwb/activity-icon-sleep.svg";
sColor = "A052C4";
iXAdjust = 43;
iYAdjust = 44;
}
%>
<%--Generate div that will hold the chart--%>
<div data-ctl="["Chart"]" id="<%=uniqueId%>" style="width:<p:r n='Param.width'/>;height:<p:r n='Param.height'/>" data-loadmode="auto" data-chartsubtype="dial" data-charttype="gauge" data-library="fusion"></div>
<%--Initialize the chart using pxChart's initializer--%>
<script>
Charts.initSingleChart('<%=uniqueId%>',{sourcedata:{
"chart": {
"lowerLimit": "0",
"upperLimit": "<%=maxValue %>",
"showGaugeBorder": "0",
"canvasBGAlpha":"0",
"bgAlpha":"0",
"showValue": "0",
"valueBelowPivot": "1",
"showCanvasBorder":"0",
"showBorder":"0",
"usePlotGradientColor": "0",
"showTickMarks":"0",
"showTickValues":"0",
"gaugeStartAngle":"90",
"gaugeEndAngle":"-270",
"gaugeFillRatio":"0",
"gaugeFillMix":"{light-0}",
"showshadow":"0",
"pivotFillAlpha":"0",
"majorTMNumber":"0",
"minorTMNumber":"0",
"majorTMAlpha":"0",
"minorTMAlpha":"0",
"majorTMColor":"#D9DAD8",
"gaugeInnerRadius":"82%",
"chartLeftMargin":"0",
"chartRightMargin":"0",
"chartTopMargin":"0",
"chartBottomMargin":"0",
"canvasPadding":"0",
"showToolTip":"0",
"baseFont":"OpenSans"
},
"colorRange": {
"color": [
{
"minValue": "0",
"maxValue": "<%=value %>",
"code": "#<%=sColor %>"
},
{
"minValue":"<%=value+1 %>",
"maxValue": "<%=maxValue %>",
"code": "#D9DAD8"
}
]
},
"dials": {
"dial": [{
"value": "0",
"borderAlpha":"0",
"baseWidth":"0",
"topWidth":"0"
}]
},
"annotations": {
"origW":"130",
"origH":"130",
"autoscale":"1",
"scaleImages":"1",
"scaleText":"1",
"groups": [{
"items": [
{
"id":"icon",
"type":"image",
"url":"<%=sImageURL %>",
"x":"$gaugeCenterX-<%=iXAdjust %>",
"y":"$gaugeCenterY-<%=iYAdjust %>",
"xScale":"220",
"yScale":"220"
}
]
}]
}
}});
</script>
</pega:when>
<pega:otherwise>
Gauge chart, Static score out of 100
</pega:otherwise>
</pega:choose>
Updated: 17 Jun 2015 10:32 EDT
Pegasystems Inc.
GB
Nice one - @Gregory Loucas - can you just provide a little bit more context for the code above - do you just paste that directly into a new Custom Control ?
There are two parts to the above code:
- The Pega JSP code -- initializing the data coming into the control and parameters that should be pass from the section using this control
- Everything inside the <script> tag which is the JSON that is coming from Fusion's documentation (your JSfiddle that you should configure, from the link above). The idea here is that you technically could just have a custom control in Pega and display static data, but I know that what you actually want is to utilize Pega's data. So for the values that affect the data points in the bullet graph, expose those as parameters in the custom control.
Notice above, all of the "Param.xxx" calls that are passed to the control. In the case of this angular gauge we passed 5 parameters -- value, Max Value, Image type, Width and Height. End results (in Pega), below (just the chart, not the "866 steps" text):