Jun 03

A new chart type for ColdFusion

Posted by James Netherton | Sunday 03 June 2007 8:14 PM | In ColdFusion

Following up on my previous post about customizing CFCHART, I bring news of hidden charts that are not available via the standard ColdFusion CFCHART tag.

Note: The following example will only work with ColdFusion 8.

Fire up the WebCharts Java application (see my previous post on how to do this) and you'll notice that some charts types available are not supported via the CFCHART tag:

  • Gauge Charts
  • Gantt Charts
  • Heatmaps

I spent some time looking at how I could use ColdFusion to generate one of these chart types. The following example shows how to create a simple gauge chart. So without further ado, I introduce the gaugeChart custom tag:

WebCharts expects XML descriptions of the chart styles and the chart data models in order for the chart engine to output the chart image, swf etc...

<!--- Create the xml chart style structure --->
<cfsavecontent variable="style">
<cfoutput><gauge font="#attributes.font#-#attributes.fontSize#-bold">
   <knobStyle clipKnob="false" clipHand="false" size="#attributes.knobSize#">
      <paint shadowColor="##FFDDAA" minColor="#attributes.knobColor#"/>
   </knobStyle>
   <handStyle type="#attributes.dialType#" placement="Text" width="3">
      <paint shadowOffsetX="0" shadowOffsetY="0" outlineColor="#attributes.dialOutlineColor#" minColor="##AA0000" maxColor="red" angle="90"/>
   </handStyle>
   <dialStyle startAngle="#attributes.dialStartAngle#">
      <paint minColor="white" maxColor="##333333" isRadial="true"/>
   </dialStyle>

   <axisStyle scaleMax="#attributes.dialMaxScale#" angleMargin="40" outerMargin="5" limitPlacement="#attributes.dialLimitPlacement#" limitMargin="0" minorTickPlacement="Outer"> <labelStyle isMultiline="false" color="##E2FFFF"/>
      <majorTick width="4" length="12"> <paint shadowColor="black"/></majorTick>
      <minorTick width="2" length="6"/>
      <title angle="-90" ratio="90">#attributes.dialTitle#</title>
      <limits index="0" minValue="#attributes.dialLimitStart#" maxValue="#attributes.dialLimitEnd#" minWidth="12" maxWidth="12">
         <paint minColor="##AA0000" maxColor="##AA0000"/>
      </limits>
   </axisStyle>
   <edgeStyle outerSide="1" innerSize="2" innerSide="10">
      <outer minColor="##666666" angle="-135"/>
      <inner minColor="##333333" maxColor="##CCCCC" angle="45"/>
   </edgeStyle>
</gauge></cfoutput>
</cfsavecontent>

I didn't manually hack out all of that styling markup. I cheated and used WebCharts to generate it for me. It is then a simple case of replacing the styles that we need control over, with ColdFusion variables.

The next step is to define the data model. Again I used WebCharts to generate the outline for me:

<!--- Create the chart data model --->
<cfsavecontent variable="model">
<cfoutput><?xml version="1.0" encoding="UTF-8"?>
<XML type="default">
   <COL>1000</COL>
   <ROW col0="#attributes.dialValue#">Sample 0:</ROW>
</XML></cfoutput>
</cfsavecontent>

Now we are ready to output the chart to our web page:

<!--- Output the chart --->
<cfscript>
context = getPageContext();
   
chart = createObject("java","com.gp.api.jsp.MxServerComponent");

svr = chart.getDefaultInstance(context.getServletContext());

myChart = svr.newImageSpec();
myChart.width = attributes.chartWidth;
myChart.height = attributes.chartHeight;
myChart.type = attributes.chartFormat;
myChart.style = style;
myChart.model = model;

chartTag = svr.getImageTag(myChart,"/CFIDE/GraphData.cfm?graphCache=wc50&graphID=");
</cfscript>

<cfoutput>#chartTag#</cfoutput>

As ColdFusion uses the WebCharts Java API via the CFCHART tag, this API is naturally available to any ColdFusion template. We take advantage of this and instantiate an MxServerComponent object.

As you can see, the next stage builds up our chart definition via the myChart variable. You can see that the chart dimensions are being defined and we are assigning our style and data models.

To create the chart we call:

svr.getImageTag(myChart,"/CFIDE/GraphData.cfm?graphCache=wc50&graphID=");

When we do this, the chart image is written to the default chart cache directory, as defined within the ColdFusion administrator.

Finally we write out the HTML markup needed to view the chart:

<cfoutput>#chartTag#</cfoutput>

What's with the "/CFIDE/GraphData.cfm?graphCache=wc50&graphID=" path specified on the getImageTag method invocation , I hear you cry!

Again, I have cheated here to make life easier. If you view the HTML source on a web page that a CFCHART image has been embedded into, you will see that the chart image links to /CFIDE/GraphData.cfm. This URL pulls the appropriate chart data into the calling web page.

As a matter of interest, you won't find any template named GraphData.cfm within the CFIDE directory. /CFIDE/GraphData.cfm is simply a mapping to the ColdFusion charting servlet.

Now for an example of the custom tag calling code. I'm going to use the new ColdFusion 8 server monitoring API to get the amount of used JVM memory:

<cfset adminObj = createObject("component","cfide.adminapi.administrator")/>
<cfset adminObj.login("password")/>

<cfset serverMonitor = createObject("component","cfide.adminapi.servermonitoring")/>

<cfset memstats = serverMonitor.getJvmMemoryStats()/>

<cfset megabyte = 1024 * 1024/>

<cfset used = memstats.usedmemory / megabyte>

<cf_gaugeChart   chartFormat="png"
      chartWidth=200
      chartHeight=200
      dialLimitStart=450
      dialLimitEnd=500
      dialMaxScale=500
      dialTitle="JVM Memory Used"
      dialValue=#getToken(used,'1','.')#>

I won't explain all of the tag attributes here as they're all documented within the tag template file. Hopefully the tag call above is fairly self explanatory in terms of how everything works. You set the chart dimensions, some values for the chart upper and lower readout boundaries, a chart title and the format that the chart should be output in. I pipe in the amount of memory used via the dialValue attribute.

Acceptable chart formats are: PNG, SWF, SVG, PDF, EPS, JPG, GIF and TIF.

Here's an example of what the resulting chart looks like:

Guage Chart

I'll try and get some examples of creating Gantt, heatmap and treemap charts shortly. Until then, here's the custom tag.

gaugeChart.zip

 

11 Comments

[Post comment]


1

Posted by David Beale | Thursday 28 June 2007 2:16 PM

Very cool!


2

Posted by charlie arehart | Saturday 28 July 2007 10:47 AM

James, this is indeed cool, but in case anyone else misses it, folks should note that they shouldn't copy/paste the code from your code samples. :-) Those are just illustrative portions a custom tag you offer in a zip at the bottom of the entry.

I'd simply missed the mention of that near the top, and found the code as copy/pasted wasn't working for lack of many "attributes" variables not defined. That made me assume you may have them defined with some CFPARAMs that weren't shown, and that's when I looked for and found the link to the zip file. :-)

Hey, one other thing: you may want to update that zip file to have a link back to this post or at least to add your name. You deserve credit for the work if someone comes across it in the future in someone's environment. :-)


3

Posted by Bezjoe | Saturday 25 August 2007 8:38 AM

Hi, i tested your script, but i stuck with some error:
Invalid style type: gauge

Can you tell me, what is wrong?

ps: I use Coldfusion MX 7.0


4

Posted by Raffael | Monday 08 October 2007 6:43 AM

Me too, get this error (MX 7):
Invalid style type: gauge

Of course, CF doesn't support these special graph types by itself. but exactly this is the reason one needs custom tags or special implementation in a direct way to webcharts. so the "wrong" style type should not be a issue.
could you please give me (us) a hint where the problem is? do you need CF 8?


5

Posted by James Netherton | Monday 08 October 2007 5:45 PM

Yes it is a bug! Sorry about that folks...

The example above is CF 8 only as it ships with webcharts 5.1 which supports the guage chart type. CF 7 ships with webcharts 5.0 which does not support this chart type.

Sorry, I should have been a bit more thourough in my testing. I am working on a CF 7 example which uses a simiar chart called the 'dial chart'. I'll post the code up in the next couple of days.


6

Posted by Raffael | Thursday 18 October 2007 4:55 AM

Thank you James, this would really be great since I still sitting in front of the same problem. BTW, these chart images are produced in a folder called "IMAGES". Could this be a reason why no images are shown on my website when I try to use your example? I always get a red cross (like image not found by webserver).


7

Posted by Richard | Friday 26 October 2007 3:19 PM

Hi James, thanks for this article... it has been really helpful. I would like to ask a question. Have you found a way to wrap the gauges made using this method in the active content script for the flash versions? The problem is the infamous "Click here to activate" message pops up if I generate a flash based gauge (they look better to me) and since the filename is dynamic it seems impossible to use the Adobe javascript to embed the file to avoid the message. Thanks again!


8

Posted by James Netherton | Friday 26 October 2007 4:41 PM

Hi Richard,

I haven't found a way to do this (to be honest I haven't tried yet!) It's tricky because the content gets served up by the CF charting servlet, meaning you loose control over the chart file name.

I'll have a play around with the web chats API, maybe there's a way to specify the file name.....


9

Posted by Richard | Friday 26 October 2007 5:52 PM

Hey James,

I was able to get what I needed albeit not in the slickest of ways. I actually let the gauge render on the page in a hidden element then I read the id with JavaScript and build a CF_RunContent call. This pulls the recently rendered gauge from the server cache (I think), generates the JavaScript to embed the object, and displays the gauge without the "Click here to activate message". I guess the biggest downside is loading the same gauge twice on the page but I think this is better for my users than getting the activate message. If you come across a better way please let me know.

Step 1 - Insert gauge or other special chart custom tag into your page
Step 2 - Wrap the gauge in a form tag so you can find the generated ID and hide with a CSS style

<form id="GaugeForm" style="display:none;"><cf_gaugeChart2></form>

Step 3 - Insert below javascript into your page at the desired gauge location

<script type="text/javascript" charset='utf-8' src='/CFIDE/scripts/CF_RunActiveContent.js'></script>
<script type="text/javascript" charset='utf-8'>
   //Get the gauge ID
   var theGauge = GaugeForm.elements[0];
   var gauge_id = theGauge.id;

   //Replace underscores with forwardslash and period then add custom tag path
   var fullGaugePath = gauge_id.replace("Images_","Images/");
   var fullGaugePath = fullGaugePath.replace("_SWF",".SWF");   
   var fullGaugePath = "/CFIDE/GraphData.cfm?graphCache=wc50&graphID=" + fullGaugePath;

   //Call CF_RunContent and build string to call cached gauge by inserting gauge_id and fullGaugePath... CF_RunContent handles the javascript embed code
   CF_RunContent('<OBJECT classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" \r\n codebase="http://active.macromedia.com/flash2/cabs/swflash.c...;\r\n ID="'+ gauge_id + '" name="'+ gauge_id + '" WIDTH="270" HEIGHT="225">\r\n\t<PARAM NAME="movie" VALUE="'+ fullGaugePath + '"/>\r\n\t<PARAM NAME="quality" VALUE="high"/>\r\n\t<PARAM NAME="bgcolor" VALUE="#FFFFFF"/>\r\n<EMBED src="'+ fullGaugePath + '" \r\n\t\tquality="high" bgcolor="#FFFFFF" WIDTH="270" HEIGHT="225" TYPE="application/x-shockwave-flash"\r\n PLUGINSPAGE="http://www.macromedia.com/shockwave/download/index...;\r\n</EMBED>\r\n</OBJECT>');
</script>


10

Posted by James Netherton | Friday 26 October 2007 6:02 PM

Thanks Richard - Useful stuff


11

Posted by Gavin | Wednesday 20 August 2008 5:07 AM

Hi James,
I am working on a shared server, so I get the error message:
"...denied access to C:\Coldfusion8\charting\cache".
Any ideas on how to get around this?

Leave a comment








Captcha text