Introducing DojoX DataChart

By on March 30, 2009 12:00 am

The latest addition in the Dojo 1.3 release is the new dojox.charting class, DataChart. Its primary purpose is to make connecting a chart to a Data Store a simple process. There are also other benefits with DataChart: less parameters are needed to create a basic chart, and more defaults and convenience methods get you up and running quickly with Dojox Charting.

The Data Store

Creating a chart is fast and easy, but before we can do so, we need our data. We’ll use dojo.ItemFileWriteStore, since it is the most commonly used, and most standard store. We’ll connect to the dojox/charting/tests/stock.json file which contains a set of imaginary stock market prices. The following code should connect to that JSON file and log all of the store items to the console:

dojo.require("dojo.data.ItemFileWriteStore");
        
var store = new dojo.data.ItemFileWriteStore({ 
	url:dojo.moduleUrl("dojox.charting", "tests/stock.json") 
});
// testing
store.fetch({query:{name:"*"}, onComplete: function(items){
	console.log("fetched:", items);
}});

Fast Chart Setup

Now that we have our Data Store, we can create our chart. Start with an HTML node, formatted to the size you want the chart to be. I’m sizing the node inline, but you could use a style and class name just as well:

We naturally have to wait for the DOM to load, so in dojo.addOnLoad, we’ll place the chart code. In its most basic, default form, it requires no parameters, besides the node id. The store can be set in the parameters or as a separate method as shown. The setStore method is the same as used elsewhere in Dojo, like in the DataGrid. Unlike the Grid, the DataChart needs one additional argument — the store item property that will be charted. In this case, that property is historicPrice.

dojo.require("dojox.charting.DataChart");
dojo.addOnLoad(function(){
	chart = new dojox.charting.DataChart("chartDiv", {});
	chart.setStore(store, {symbol:"*"}, "historicPrice");
});

Chart01

And that’s all it takes to get up and running with charts.

DataChart Mapping

Now let’s look at a (trimmed down) version of the JSON file we are using, as it is important to the next concepts we will discuss:

{ "identifier": "symbol", "idAttribute":"symbol", "label": "symbol","items": [
	{ "symbol":"ANDT", 
		"historicPrice":[0.01,3.52,3.66,3.11,3.90,3.11,3.11], "price":3.52},
	{ "symbol":"ATEU", 
		"historicPrice":[6.72,6.76,6.61,6.41,6.31,6.99,7.20], "price":6.76},
	{ "symbol":"BGCN",
		"historicPrice":[4.11,3.98,4.05,4.20,4.16,4.22,3.80], "price":3.98},
	{ "symbol":"BAYC",
		"historicPrice":[9.79,9.60,9.50,2.23,9.45,9.76,9.99], "price":9.60},
	{ "symbol":"CRCR",
		"historicPrice":[8.44,8.44,8.54,8.60,9.65,8.42,8.44], "price":8.44},
	{ "symbol":"DTOA",
		"historicPrice":[2.11,2.47,3.11,3.06,3.01,3.01,3.00], "price":2.47}
]}

The property we used for our default chart is historicPrice and as you can see, it’s an array. Because the default chart is a line-type, and plots each item separately, more than one bit of data is necessary to span across the x-axis. In our case, there are seven slots in the array, which is why we had an x-axis span of seven. If we charted the price property, we would have seven points all along the y-axis, and the chart x-axis would only have a length of one.

However, the need of the chart may not be a series of individual item properties, it may be single item properties compared to the others. We can compare the price of each item on a line chart using the comparative property:

dojo.addOnLoad(function(){
	chart = new dojox.charting.DataChart("chartDiv", {
		comparative:true
	});
	chart.setStore(store, {symbol:"*"}, "price");  //  <-- single value property
});

Chart02

Chart Labels

Those x-axis labels could stand to be a little more descriptive. Fortunately, DataChart has a convenient method for applying the store item labels. We can edit the chart.xaxis object, which is exposed so that besides the property we will add here, you can edit the standard chart axis properties as shown in the comments of the code.

labelFunc is actually a method that you can tap into to write custom labels. In our case, we are going to give it a string, which tells DataChart to use its seriesLabels method. Simply put, it connects the item labels to the chart labels.

dojo.addOnLoad(function(){
	chart = new dojox.charting.DataChart("chartDiv", {
		comparative:true,
		xaxis:{labelFunc:"seriesLabels"},
	});
	chart.setStore(store, {symbol:"*"}, "price");
});

Chart03

Chart Legends

Let's backtrack for a minute here. What about that first chart we created? The x-axis in that case more closely represented time, not the items (and if you wanted to put different times on the x-axis you could write a custom label function that did that using xaxis.labelFunc — but that is beyond the scope of this article). What we need now is a legend. DojoX Charting comes with dojox.charting.widget.Legend to make it easy to add legends to your charts. Currently however, it isn't designed to handle "live" data — it's expecting that the chart knows the data as it's created. It doesn't mean we can't use it, we just need a little extra code to connect it when the chart has data:

dojo.addOnLoad(function(){
	chart = new dojox.charting.DataChart("chartDiv", {});
	chart.setStore(store, {symbol:"*"}, "historicPrice");
	
	 var c = dojo.connect(chart, "onData", function(){
		dojo.disconnect(c);
		new dojox.charting.widget.Legend({chart:chart}, "legend"); 
	});
});

Chart04

In the previous example, we connected to the chart's onData event (we connected once by disconnecting it immediately after the first call) and then create the legend programatically, now that the chart knows its data.

Different Chart Types

So what about other chart styles? It's a simple matter of passing a type:

dojo.addOnLoad(function(){
	chart = new dojox.charting.DataChart("chartDiv", {
		type: dojox.charting.plot2d.Columns, // <-- chart type
        	scroll:false,
        	xaxis:{labelFunc:"seriesLabels"}
	});
	chart.setStore(store, {symbol:"*"}, "price");
});

Chart05

Hey, wait a minute! What is scroll:false?

Animation

The DataChart supports information updating, which allows it to animate. To update the information, you change the item property in the store. The DataChart is connected to the onSet event in the store and reflects the change. The test in the DojoX trunk demonstrates this with spinner widgets that modify the store item property.

The scroll parameter works in conjunction with two other parameters:

  • stretchToFit: This is how native charts work. If given five bits of data, the x-axis will be five segments long.
  • displayRange: If set, stretchToFit is overridden and the x-axis will not stretch, it will always be that long. This is often used with scroll.
  • scroll: If true and there is more data than fits on the x-axis, it will scroll to the left.

Note that the y-axis is always fixed, having it stretch to fit or scroll is not currently supported.

Demo

I know that the combination of these parameters can be confusing, especially when it comes to different chart types. I've created a demo that you can use to test the different parameters and dynamically build new charts. It not only shows off the features of DataChart, but will get you up and running that much faster!

Note: Learn more about the new DojoX Data Chart and more at one of our upcoming workshops in Sydney, Munich, London, Milan, or Paris.

Comments

  • Pingback: SitePen Blog » Stocker: Advanced Dojo Made Easy()

  • Thomas

    Does this work with the dojox.data.XmlStore ?

  • @Thomas:
    It should. The idea is that when you get components to work with ItemFileWriteStore, they should work with all the others.

  • Thomas

    @mwilcox: Is there a concrete example for connecting this component to xml stores? I am having trouble getting the DataChart component working with the dojox.data.XmlStore.

  • @Thomas:
    So I looked into xmlStore, which I had never used before. It seems to have it’s own interface – returning xmlItems instead of numbers or strings. There’s an attributeMap that might help. I’m not versed on xmlStore, so if you need help on that, please look to the forums.

    As for DataChart, I fortunately made a convenience method that grabs values: getProperty(item, prop)

    You can overwrite this method and change it up to do the double-get that the xmlStore seems to require:

    var val = store.getValue(item, prop);
    return store.getValue(val, “text()”);

  • Conar

    is there a way to have the grid self configure to an ever changing datastore? I would like to set it up so that there can be a dropdown to select different chart options (pie, lines, etc.) and have it grab its data either from selected rows, range of cells, the whole datastore, or even better to be able to choose between these options.

  • How can I get the xaxis labels from the json string (not compared)?
    Can you give me a short example, because I want do get the data including the labels from a database.

  • Prog,
    You can set the labels directly as in the charting/test_DataChart.html:
    xaxis:{labels:[“0”, “A”,”B”,”C”,”D”,”E”,”F”,”G”,”H”,”I”,”J”]}

    Or you you can make a custom function if the need to update them:
    xaxis:{labelFunc:myCustomFunction}

  • Thanks,
    it works with the following code:
    xaxis:{labels:colorData.mylabel}
    // colorData is the JSON I get from the database

  • cédric

    I have an x-axis that takes 1000 to value. How can i change the range of this axis?

  • cédric

    Sorry, I found the answer:
    In fact it was about the y-axis and i had to add option:
    {max:1000}

  • dagarwal

    I was able to integrated the DataChart in my app successfully but it works for Mozilla and Safari only. Its frustrating that it doesn’t work with IE (checked with 6, 7, 8). Tried viewing https://user.sitepen.com/~mwilcox/Chart/DataChart.html sample page in IE and the problem is visible. Is it something which can be fixed in nightly or is it some significant amount of work?

  • @dagarwal:

    I found the problem. You need to remove the DOCTYPE from the top of the document. This screws up IE8 in particular, but it doesn’t work well with any vector graphics.

    I found a few minor things wrong which I’ll commit (and I’ll remove the DOCTYPE from the tests).

  • Carl

    Is it possible to plot multiple data series from the same datastore on one chart? For example, if I have a datastore such as:

    { “identifier”: “id”, “idAttribute”:”id”, “label”: “Date”,”items”: [
    {
    “id”: 1,
    “Date”:”2009-01-01″,
    “HitsFromFireFox”:46,
    “HitsFromSafari”:25,
    “HitsFromIE”: 120
    }
    ]}

    ….could I plot the hits from the various browsers as three data series on one chart over time?

  • @Carl:

    I’m sorry there’s currently no easy way to do this without changing your data. But it a feature that should be in there so I’ve created an enhancement ticket for it:
    http://bugs.dojotoolkit.org/ticket/9739

  • Is horizontal scrolling working for anyone?

    I have 24 data points and set

    displayRange:10,
    scroll: true

    The displayRange works, but no scrolling action.
    Am I missing something?

  • Try stretchToFit, comparative and displayRange, like shown in the demo:
    https://user.sitepen.com/~mwilcox/Chart/DataChart.html

  • Jeff

    My HTML file can be accessed from:
    “http://userwww.sfsu.edu/~jharan/agis_gmaps/dataChart.html”

    I’m just now learning dojo via the ArcGIS Javascript API, so please bear with me….

    When I try the above example by loading the stocks.json from my local machine and loading dojo via ESRI’s CDN (http://serverapi.arcgisonline.com/jsapi/arcgis/?v=1.5) firebug throws the following exception:

    “Access to restricted URI denied” code: “1012” nsresult: “0x805303f4 (NS_ERROR_DOM_BAD_URI)” location: “http://serverapi.arcgisonline.com/jsapi/arcgis/?v=1.5 Line: 16”] File: Line: 0 Column: 0

    I believe this is a result of the cross domain issue. Is it looking for the JSON file in the CDN?
    Do I need to use JSONP or some other cross domain method to access the JSON file from “dojox/charting/tests/stock.json”?

  • HI Jeff,

    Yes, you have two different domains going there and that won’t work. JSONP should get around it.

  • wirelessdreamer

    Could you add an example to use the function that converts from an array of labels to labels.

    example: for the first chart on https://user.sitepen.com/~mwilcox/dojotoolkit/dojox/charting/tests/test_DataChart.html. Instead of specifying the labels when the chart was programatically created, extract x axis names, and the y value ranges from the datastore.

    I can’t find the right way to apply these labels and set the y range for the chart.

    example json:

    {

    “idAttribute” : “symbol”,

    “identifier” : “symbol”,

    “label” : “symbol”,

    “items” : [

    {

    “count” : [

    150,

    150,

    300,

    400,

    300,

    800,

    700,

    800,

    706,

    794,

    734,

    766,

    753,

    747,

    775,

    728,

    797,

    721,

    779,

    644,

    761,

    740,

    794,

    725,

    776,

    739,

    765,

    767,

    737,

    777,

    736,

    236

    ],

    “uniquename” : “123456789-test-15-5”,

    “symbol” : “123456789”,

    “name” : “123456789”,

    “name” : “test”,

    “current” : 236,

    “labels” : [

    “15:5”,

    “15:6”,

    “15:7”,

    “15:8”,

    “15:9”,

    “15:10”,

    “15:11”,

    “15:12”,

    “15:13”,

    “15:14”,

    “15:15”,

    “15:16”,

    “15:17”,

    “15:18”,

    “15:19”,

    “15:20”,

    “15:21”,

    “15:22”,

    “15:23”,

    “15:24”,

    “15:25”,

    “15:26”,

    “15:27”,

    “15:28”,

    “15:29”,

    “15:30”,

    “15:31”,

    “15:32”,

    “15:33”,

    “15:34”,

    “15:35”,

    “15:36”

    ],

    “yaxis” : 880

    }

    ]

    }

  • @ wirelessdreamer

    I think what you are asking for is the labelFunc method in the yaxis:

    chartLines = new dojox.charting.DataChart(“lines”, {
    type: dojox.charting.plot2d.Markers,
    yaxis:{
    labelFunc: “seriesLabels”
    }
    });

    “labelFunc” expects a function normally, “seriesLabels” is a DataChart-specific helper function. You could also do something custom:

    chartLines = new dojox.charting.DataChart(“lines”, {
    type: dojox.charting.plot2d.Markers,
    yaxis:{
    labelFunc: function(axisNumericalPosition){
    // get store element or property here, based on the axisNumericalPosition, which
    // will fire for each element or “tick” of your axis. If you just return it, it would be
    // 0,1,2,3,4… etc.
    }
    }
    });

  • Chandan

    Is there a way to build a chart similar to the one on itunes – when you select your device (iphone/ipod) a single horizontal bar chart appears which splits the total capacity based on the space consumed by components like audio,photos,apps etc

  • Chandan, that would be StackedBars or StackedColumns. I’m not sure if that works with DataChart… I think it should. If not please file an enhancement request. See the charting tests for 2DChart.

  • Chandan

    Thanks mwilcox, I will try that. I have a different question this time –

    Currently the legend widget for my data chart (Pie) always displays percentages. How can I make this to display stock “price”. I have the below script in my addOnLoad function. My html had “legend_pie” div.

    var store = new dojo.data.ItemFileWriteStore({
    url:dojo.moduleUrl(“”, “../pages/users/data/stock.json”)
    });
    var dc = dojox.charting;
    Chart = new dc.DataChart(“chartDiv”, {
    type: dojox.charting.plot2d.Pie, // < chart type
    comparative:true
    });

    Chart.setStore(store, {symbol:"*"}, "price"); // < — single value property
    var anim_a = new dc.action2d.MoveSlice(Chart, "default");
    var anim_b = new dc.action2d.Highlight(Chart, "default");
    var anim_c = new dc.action2d.Tooltip(Chart, "default");
    Chart.render();
    var legend_pie = dojo.connect(Chart, "onData", function(){
    dojo.disconnect(legend_pie);
    new dojox.charting.widget.Legend({chart:Chart}, "legend_pie");
    });

  • Hi Chandan,

    Sorry, it looks like percentages is baked into the Pie Chart and/or the Legend. The only way to change this is to change the code. You could overwrite either Legend.refresh or Legend._addLabel, copying the code and editing it to suit your needs. You could also attach to it:

    myLegend._oldAddLabel = myLegend._AddLabel

    myLegend._AddLabel = function(dyn, label){
    // strip the % off of label and reinsert it
    }

    But I’m afraid either way is kludgey.

  • Michael Ticknor

    Hi, the demo link – https://user.sitepen.com/~mwilcox/Chart/DataChart.html – is not working. Thanks!

  • wirelessdreamer

    Thanks for reply before. Any plans to make it so the y axis auto adjusts, thats been my main blocker to using DataChart, as data could easily jump from 50 to 3000, and adding logic to rebuild the graph every time that happens requires as much work as managing updates on your own using normal dojo charting, which makes DataChart not as useful as it is intended to be.

    Thanks again for the work, <3 DataGrid and persevere together

  • anil

    hi,
    i am strugggling to draw a graph since i have some data that flows from my server side through an XML (typical AJAX) embeded in a JSP. So all that i have is a string that is comma seperated values and i have to draw a graph using that.I want to rule out the option of
    String.split(‘,’); and then iterating through array and calling parseFloat () on each element.Since that is very painfull cos at times i might have as manhy as 800 members in that array.And also passing JSOn array isnt an option i have since i already have some code that is exitent since ages and uses XML as explained above and cant get rid of it…
    Can you please explain me how i can draw a graph using this data.
    Please find below the snippet of how my data might look like.And also another thing to remember is i have this data dynamically flowing not static…

    Please help
    regards
    Anil

  • anil

    sorry this is how my data looks like

    seriesB= “2.6, 1.8, 2, 1, 1.4, 0.7, 2”;

  • vlad

    is it possible to render these charts on webkit on the server side and get the resulting PNGs and send them to users attached via email

    (I need to have charts that are generated on users Web ui, also to be sent to the by end of week in an email)

    thank you in advance

  • Hi,

    Great work and great article !

    In the current state of situation, it’ seems you can only directly input data from the data store to the graph. One thing that I’d like to do would be to make calculations based on the datastore, and send these to the chart instead of the datastore directly.

    Let me explain : I have a datastore of sales (like : salesRepresentative, salesAmount, salesProduct, salesOpened, salesClosed…). My wish would be to be able to make a chart of these stats :
    – evolution of the average of days needed to close a sale (salesOpened – salesClosed) ; show this on a line
    – the nb of sales opened the last x days (x beeing able to be set by user) ; show a stackedBar chart
    – etc.

    I’m not sure it’s possible to do this actually but it would be a great enhancement, since a lot of us are pumping data from dataStores, we would benefit greatly from updates from the initial stores

    Thanks a lot for this work it’s super cool anyway

  • Hi,

    I want to put the label vertical, is it possible? Thanks

    Code:

    BarChart.addAxis(“x”, {min: 1, max:24,microTicks: false, majorTickStep: 1,microTicks: false,minorTicks: false,
    labels: [{value: 1, text: “1h 1/02/2011”}, {value: 2, text: “2h 1/02/2011”},
    {value: 3, text: “3h 1/02/2011”}, {value: 4, text: “4h 1/02/2011”},
    {value: 5, text: “5h 1/02/2011”}, {value: 6, text: “6h 1/02/2011”},
    {value: 7, text: “7h 1/02/2011”}, {value: 8, text: “8h 1/02/2011”},
    {value: 9, text: “9h 1/02/2011”}, {value: 10, text: “10h 1/02/2011”},
    {value: 11, text: “11h 1/02/2011”}, {value: 12, text: “12h 1/02/2011”},
    {value: 13, text: “13h 1/02/2011”}, {value: 14, text: “14h 1/02/2011”},
    {value: 15, text: “15h 1/02/2011”}, {value: 16, text: “16h 1/02/2011”},
    {value: 17, text: “17h 1/02/2011”}, {value: 18, text: “18h 1/02/2011”},
    {value: 19, text: “19h 1/02/2011”}, {value: 20, text: “20h 1/02/2011”},
    {value: 21, text: “21h 1/02/2011”}, {value: 22, text: “22h 1/02/2011”},
    {value: 23, text: “23h 1/02/2011”}, {value: 24, text: “24h 1/02/2011”}]
    });

  • Mohammed safeer

    Hi is it possible to plot data from multiple stores on same chart. so that it will be easy to compare two stores..

    thanks in advance…

  • Anna

    Great article!

    But I don’t understand how can I create a chart based on a dataChart width declarative tag html code.

    Can someone help me please.

    thanks

  • Rishi

    i need a help from u guys.i need to make a stacked bar chart using json and in that there different checkbox for all bar.when i uncheck the check box corresponding bar should be removed from the chart.can any pls help me with this.

  • pazany moorghen

    hi,
    is it possible to show custom label on the data point instead if the coordinates ?
    for example

    chart1.addPlot(“thresholdcurrent”, { type: “Lines”, labels:false, labelStyle: “outside” });

    chart1.addSeries(“thresholdcurrent”, [{ x: 4, y: 1 }, { x:4, y: 1.5 }], { plot: “thresholdcurrent” });

    the labe displayed is 1 and 1.5 on the line

  • Hi @pazanymoorghen:disqus , it’s certainly possible, but I don’t think this is an out of the box API. Instead I think you need to write a short extension to customize it. We’re happy to help with that under SitePen support if you need hands-on help ( http://sitepen.com/support/ ), or you could ask for guidance on dojo-interest, http://mail.dojotoolkit.org/mailman/listinfo/dojo-interest , for some basic pointers to get you started!