A reader emailed me earlier this week and suggested I take a look at Highcharts, a JavaScript charting solution created by Torstein Hønsi out of Norway. It's an interesting package and I'm glad the reader suggested I take a look.
First off - let's talk costs. Highcharts is free for non-commercial use. That's great. The commercial license is a bit odd though. For 80 dollars you get a single site license but the site "does not have customer specific data or charges for its use." I'm not quite sure what to make of that. Customer specific data could be anything from a simple login account to a cookie for your favorite background color. Above that you've got a single developer license. This also seems weird. They say a developer is :"Each person who directly or indirectly creates an application or user interface containing Highcharts is considered a developer." So according to this, if my coworker adds a chart I'm not allowed to edit it if I find a typo. For a JavaScript package I just don't think this makes sense. That being said - the prices (80 for the single site and 360 for the single developer) don't seem too terribly high for what you get. I encourage you to read the license page. Maybe it will make more sense to you then it did to me.
Feature wise you get the normal gamut of support chart types. Nothing stands out here as exception or missing so that's good. However, the examples look very professional. I did a quick comparison to jqPlot and I think Highcharts looks significantly better. It also ships with a couple of built in themes which all look very well done.
Usage follows pretty much every other framework out there. Include jQuery (you can also make it work with Mootools), include the Highcharts library, and then instantiate a chart object. Here is a complete example taken from their documentation:
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script src="Highcharts-2.1.2/js/highcharts.js"></script>
<script>
var chart1; // globally available
$(document).ready(function() {
chart1 = new Highcharts.Chart({
chart: {
renderTo: 'chart-container-1',
defaultSeriesType: 'bar'
},
title: {
text: 'Fruit Consumption'
},
xAxis: {
categories: ['Apples', 'Bananas', 'Oranges']
},
yAxis: {
title: {
text: 'Fruit eaten'
}
},
series: [{
name: 'Jane',
data: [1, 0, 4]
}, {
name: 'John',
data: [5, 7, 3]
}]
});
});
</script>
</head>
<body>
<div id="chart-container-1" style="width: 400px; height: 400px"></div>
</body>
</html>
You can view that code here. Modifying the theme is pretty cool - instead of using an object you can just include another JavaScript file, ie:
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script src="Highcharts-2.1.2/js/highcharts.js"></script>
<script src="Highcharts-2.1.2/js/themes/dark-green.js"></script>
<script>
var chart1; // globally available
$(document).ready(function() {
chart1 = new Highcharts.Chart({
chart: {
renderTo: 'chart-container-1',
defaultSeriesType: 'bar'
},
title: {
text: 'Fruit Consumption'
},
xAxis: {
categories: ['Apples', 'Bananas', 'Oranges']
},
yAxis: {
title: {
text: 'Fruit eaten'
}
},
series: [{
name: 'Jane',
data: [1, 0, 4]
}, {
name: 'John',
data: [5, 7, 3]
}]
});
});
</script>
</head>
<body>
<div id="chart-container-1" style="width: 400px; height: 400px"></div>
</body>
</html>
You can view that demo here. What I find cool is how nicely the new theme changed the default colors to work well with the new background.
Another interesting feature of this framework is that it supports exporting. You can export to PDF, PNG, and JPG. This is done in a slick way. You can either point to his own web server or you can point to a PHP file on your own system. Now to be clear - this is not the same as being able to get a real binary copy of the chart. For example, ColdFusion's built in charting allows you to generate straight to an image that can be stored, emailed, or printed even. All JavaScript based charting engines will still require you to have a real browser to render and execute the code to get the display.
I worked on a simple ColdFusion example and here is where I ran into my only real problem. I don't feel like the documentation for data formats is very clear. The main documentation page is really just an introduction. The examples all give you a quick way to see the code but are just - examples. There is a reference guide as well. But in terms of using data it seems like you have to guess a bit. I know it was a bit rough for me. Then again - I worked on this for thirty minutes so with more practice I think it would get easier. Here is the ColdFusion sample I came up. It's not Ajax based (which Highcharts supports of course) but it is dynamic.
<cfset cats = []>
<cfset data = []>
<cfloop query="getArtStats">
<cfset arrayAppend(cats, media)>
<cfset arrayAppend(data, total)>
</cfloop> <html> <head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script src="Highcharts-2.1.2/js/highcharts.js"></script>
<script> var chart1; // globally available
$(document).ready(function() {
chart1 = new Highcharts.Chart({
chart: {
renderTo: 'chart-container-1',
defaultSeriesType: 'bar'
},
title: {
text: 'Art by Media'
},
xAxis: {
categories: <cfoutput>#serializejson(cats)#</cfoutput>
},
yAxis: {
title: {
text: 'Total'
}
},
series: [{name:"Totals",data:<cfoutput>#replace(serializejson(data),"""","","all")#</cfoutput>}] });
});
</script>
</head> <body> <div id="chart-container-1" style="width: 400px; height: 400px"></div> </body>
</html>
<cfquery name="getArtStats" datasource="cfartgallery">
select count(a.artid) as total, m.mediatype as media
from art a
join media m
on a.mediaid = m.mediaid
group by a.mediaid, m.mediatype
</cfquery>
You can view this here. So - thoughts? I know I've seen a few people recently tweet about Highcharts so I'd love to see some comments from folks using it. (And please - if you have used it and can share a URL, please do. I'd love to see real life examples.)
Archived Comments
The primary reason I'm avoiding javascript-based charting scripts is because CFDocument can't render them in a PDF. Almost every client I have also wants to be able to save the information and use in a presentation. There's no way to right-click the graphs that Highcharts creates and save to your hard drive (like some other javascript+canvas charts.)
I'm using ChartDirector for ColdFusion:
http://www.advsofteng.com/c...
It comes with lots of sample CF code to create multiple types of charts too.
http://www.advsofteng.com/g...
NOTE: If you don't register, it only adds a notice to the bottom 20 pixels of the image.
Eh? As I stated - you can - on the client - export to PDF. It isn't 100% server side ala cfchart - but in your specific example "right click save as" you CAN do that.
Sorry... I didn't see the extra buttons at the upper-right of your charts for downloading. (Your chart didn't have it, but some of their demos did.) Thanks for restating it.
They should consider usurping the right-click menu when hovering over the chart since it's useless otherwise. Some other graphing components I've used allow you to do this or create a canvas graphic that can be saved (with no additional server-side magic required.)
NOTE: The link to the "reference" doesn't end and the link continues for the entire remaining paragraph.
Link fixed. Thanks!
Looks pretty good. I use fusion charts for all my charting needs. http://www.fusioncharts.com/ .
I find it really awesome because i can pass in data in XML format and get stunning flash charts.
I have dropped using Highcharts because I have had some problems where it was displaying fine in FF and any webkit browser but not so great in IE7/8 doing simple stuff. Bummer cause it is a cool lib.
@John Allen -
I've been evaluating Highcharts for a small commercial firm I work with and it appears to be able to render the timeseries financial charts (XY line charts) properly in all of the current major browsers including IE8. I particularly like its responsiveness over other JS libraries I've looked at.
If you could give me a couple of specifics where it didn't work for you that would help me with my continuing evaluation. Thanks.
Hi,
I actually tried both Fusion Charts and HighCharts for the last project that I did, but finally settled with HS. For some reason, the FC chart breaks when it's loaded inside a CFLayout (boo!). Fusion Charts is great but when you load multiple charts (big ones) on a report, it just slower than HighCharts. HC also scales nicely on different screen sizes,including ipad...
Here's some screenshots of the charts in action. http://opy.tumblr.com/post/...
One of the things I haven't had a chance to try yet is with point Highcharts Exporting / Printing module to the Coldfusion server itself. I believe the Highcharts site uses Batik to covert the SVG data from Highcharts for Exporting and Printing. Digging around on Google, I found an older article describing how to install the full version of Batik on the Coldfusion server. http://www.barneyb.com/barn... If that works, you should be able to point the Highcharts Exporting / Printing module there and handle those duties internally.
Though not out yet, extJS 4 will support charting.
http://www.sencha.com/blog/...
Looks to be a very robust solution.
Too bad I hate Ext. ;) (Kidding, I don't hate it- but it's definitely not a framework I like to use.)
I'm sure that asking why you don't use it is off topic but...have you explained why in another post that I could read? Or perhaps you could go off topic and briefly explain?
I don't mind going a bit OT. I just don't like the syntax - the way it does stuff. The end result is _damn_ good. Ext makes some very powerful stuff. And I think Ext does "Application UI" the best. But for actually using it - it just doesn't feel right to me. Totally a personal decision and not a 'it sucks' type thing.
Interesting points. I think how you point out that it excels at "Application UI" is key. I originally heard of it and thought hey, alternative to jQuery ( I still love jQuery )! Though I suppose in some areas, it is an alternative, it definitely has had a completely different use.
As far as using it, I think the fact that CF integrates it so easily has made me using it for basic stuff like grids a no-brainer.
@roger
Thanks for asking cause it made me ask the other developer about the issue and he said: problems all fixed.
SO PLEASE DISREGARD MY DUMB COMMENT ABOVE.
Hi Ray,
I try to emulate CFC on highchart which originally create on PHP to retrieve stock price. Since my project require Coldfusion. So, I try to create CFC that return stock price. Unfortunately, since im newby on JSON.. im failed to load the highchart on my page.
Can u advise me what is wrong with the JSON calling or my return CFC code.
Original Source code =
$.getJSON('http://www.highcharts.com/s..., function(data) { ...
MY Source code =
$.getJSON('http://69.67.30.20/projects/json/testcomp.cfc?method=getprice&UID=09740BD8-4FB8-4149-99725DBB8383AB47&returntype=json', function(data) { ...
Below is my CFC sample file =
<cfcomponent displayname="test" hint="ColdFusion Component for Fund Price">
<cfset response = getPageContext().getResponse()>
<cfcontent type="application/json">
<cffunction name="getprice" access="remote" returnformat="json" output="false">
<cfargument name="UID" required="true">
<cfset fundReturns = "[[Date.UTC(2000, 0,21), 0.00],[Date.UTC(2000, 0,22), 0.00],[Date.UTC(2000, 0,23), 0.00],[Date.UTC(2000, 0,24), 0.00],[Date.UTC(2000, 0,25), 0.00],[Date.UTC(2000, 0,26), 0.00],[Date.UTC(2000, 0,27), 0.00],[Date.UTC(2000, 0,28), 0.00],[Date.UTC(2000, 0,29), 0.00],[Date.UTC(2000, 0,30), 0.00],[Date.UTC(2000, 0,31), 0.00],[Date.UTC(2000, 1, 1), 0.00],[Date.UTC(2000, 1, 2), 0.00],[Date.UTC(2000, 1, 3), 0.00],[Date.UTC(2000, 1, 4), 0.00],[Date.UTC(2000, 1, 5), 0.00],[Date.UTC(2000, 1, 6), 0.00],[Date.UTC(2000, 1, 7), 0.00],[Date.UTC(2000, 1, 8), 0.01],[Date.UTC(2000, 1, 9), 0.02],[Date.UTC(2000, 1,10), 0.02],[Date.UTC(2000, 1,11), 0.03],[Date.UTC(2000, 1,12), 0.03],[Date.UTC(2000, 1,13), 0.03],[Date.UTC(2000, 1,14), 0.06],[Date.UTC(2000, 1,15), 0.07],[Date.UTC(2000, 1,16), 0.08],[Date.UTC(2000, 1,17), 0.09],[Date.UTC(2000, 1,18), 0.09],[Date.UTC(2000, 1,19), 0.09],[Date.UTC(2000, 1,20), 0.09],[Date.UTC(2000, 1,21), 0.11],[Date.UTC(2000, 1,22), 0.12],[Date.UTC(2000, 1,23), 0.13],[Date.UTC(2000, 1,24), 0.14],[Date.UTC(2000, 1,25), 0.14],[Date.UTC(2000, 1,26), 0.14],[Date.UTC(2000, 1,27), 0.14],[Date.UTC(2000, 1,28), 0.16],[Date.UTC(2000, 1,29), 0.17],[Date.UTC(2000, 2, 1), 0.18],[Date.UTC(2000, 2, 2), 0.19],[Date.UTC(2000, 2, 3), 0.20],[Date.UTC(2000, 2, 4), 0.20],[Date.UTC(2000, 2, 5), 0.20],[Date.UTC(2000, 2, 6), 0.24],[Date.UTC(2000, 2, 7), 0.24],[Date.UTC(2000, 2, 8), 0.25],[Date.UTC(2000, 2, 9), 0.26],[Date.UTC(2000, 2,10), 0.27],[Date.UTC(2000, 2,11), 0.27],[Date.UTC(2000, 2,12), 0.27],[Date.UTC(2000, 2,13), 0.24],[Date.UTC(2000, 2,14), 0.25],[Date.UTC(2000, 2,15), 0.26],[Date.UTC(2000, 2,16), 0.26],[Date.UTC(2000, 2,17), 0.28],[Date.UTC(2000, 2,18), 0.28],[Date.UTC(2000, 2,19), 0.28],[Date.UTC(2000, 2,20), 0.08],[Date.UTC(2000, 2,21), 0.09],[Date.UTC(2000, 2,22), 0.10],[Date.UTC(2000, 2,23), 0.11],[Date.UTC(2000, 2,24), 0.12],[Date.UTC(2000, 2,25), 0.12],[Date.UTC(2000, 2,26), 0.12],[Date.UTC(2000, 2,27), 0.22],[Date.UTC(2000, 2,28), 0.20],[Date.UTC(2000, 2,29), 0.19],[Date.UTC(2000, 2,30), 0.20],[Date.UTC(2000, 2,31), 0.20],[Date.UTC(2000, 3, 1), 0.20],[Date.UTC(2000, 3, 2), 0.20],[Date.UTC(2000, 3, 3), 0.24],[Date.UTC(2000, 3, 4), 0.34],[Date.UTC(2000, 3, 5), 0.35],[Date.UTC(2000, 3, 6), 0.35],[Date.UTC(2000, 3, 7), 0.37],[Date.UTC(2000, 3, 8), 0.37],[Date.UTC(2000, 3, 9), 0.37],[Date.UTC(2000, 3,10), 0.42],[Date.UTC(2000, 3,11), 0.43],[Date.UTC(2000, 3,12), 0.44],[Date.UTC(2000, 3,13), 0.46],[Date.UTC(2000, 3,14), 0.44],[Date.UTC(2000, 3,15), 0.44],[Date.UTC(2000, 3,16), 0.44],[Date.UTC(2000, 3,17), 0.50],[Date.UTC(2000, 3,18), 0.51],[Date.UTC(2000, 3,19), 0.65],[Date.UTC(2000, 3,20), 0.54],[Date.UTC(2000, 3,21), 0.61],[Date.UTC(2000, 3,22), 0.61],[Date.UTC(2000, 3,23), 0.61],[Date.UTC(2000, 3,24), 0.61],[Date.UTC(2000, 3,25), 0.62],[Date.UTC(2000, 3,26), 0.71],[Date.UTC(2000, 3,27), 0.68],[Date.UTC(2000, 3,28), 0.74],[Date.UTC(2000, 3,29), 0.74]]">
<cfreturn fundReturns>
</cffunction>
</cfcomponent>
Regards,
Seriously CF newbie
I see 2 main issues.
1) In their example, they are making use of JSON/P to get data from a remote host. This requires a specific syntax under jQuery, most notably the callback=? at the end of the URL. Your code does not use this, but it uses a full host name for the URL. If your code is local, fine, then change the URL to a relative path.
2) The biggest issue is that you are returning a string, not data. (OK, technically a string can be data.) When you call a remote CFC method and add returnformat=json, you tell CF to take _whatever_ is returned and serialize it for JSON. Your method is returning a JSON string already! So you are JSON serializing JSON. You have 2 options.
a) Use returnformat=plain: This says, "CF, don't do squat."
b) Most likely you will eventually want to return 'real' data. When you do, returnFormat=json makes sense.
p.s. In the future, please use Pastebin to share code.
p.s.s. I'll be playing with HIghcharts soon on mobile w/ PhoneGap. Just sharin'.
Hi Ray,
I get it right by placing evaluate function based on your hint.
$.getJSON('testcomp.cfc?meth...', function(data) { ...
//evaluate string from CFC
var data = eval('(' + data + ')');
Thank you very much.
BTW, Looking forward for your PhoneGap Highcharts project.
Regards,
NewBie
To be clear, while it works (eval(json)), you really want to be working with _real_ data and not using the eval.
Hi NewBie,
I don't know if you're still around, but how did you loop through your query and generate the "Date.UTC" stuff? I'm having trouble formatting the data for a time series chart.
Maybe someone else has been successful with this?
Paul - this is what I used. Not elegant but it is for just a few data points. I included the 'series' object but the inner query loop is where the dates are populated.
series: [
<cfset ix = 1>
<cfloop list="#Issues#" index="IssueID">
{
type: 'line',
name: '<cfoutput>#qryIssueName.IssueName#</cfoutput>',
<cfset linName = "Line" & ix>
<cfset colName = "Price" & ix>
animation: false,
data: [ <cfloop query="Prices">
<cfset price = Prices[colName][CurrentRow]>
<cfif price EQ "">
<cfset price = 'null'>
</cfif>
<cfoutput>[Date.parse("#DateFormat(Prices['PriceDate'][CurrentRow],'mm/dd/yyyy')#"),#price#]</cfoutput>
<cfif Prices.RecordCount neq Prices.CurrentRow>,</cfif>
</cfloop>
]
}
<cfif ix NEQ ListLen(URL.Issues)>,</cfif>
<cfset ix = ix + 1>
</cfloop>
]
i am returning this variable in an ajax call:
<cfset Variables.TotalUnitsAll =replace(serializejson(Variables.TotalUnits),"""","","all")/>
this shows up as [1,2,3,4,5] but does not pick up as a javascript variable with this error: Unexpected value NaN parsing y attribute. any ideas how i can get this to pick up on the series
Sorry Kumar, not sure. If you are returning something valid to Highcharts and it isn't working, you may want to hit up their support options.
Thank you, you are genius. You just saved me a lot of time.
Not a genius - just someone who blogs quicker than he can think. ;)