ZoomCharts Documentation

PieChart / FacetChart Data

Pie Chart and Facet Chart share the same data structure, therefore following documentation applies to both of these chart types.

Data format

A typical data object looks like this:

{
    "subvalues": [
        { name: "First slice", value: 1 },
        { name: "Second slice", value: 2 },
        {
            name: "Third slice, expandable",
            value: 3,
            subvalues: [
                { name: "First subslice of third slice", value: 1 },
                { name: "Second subslice of third slice", value: 2 },
                { name: "Third subslice of third slice", value: 3 },
            ]
        },
    ]
}

There is a difference in available properties between the root object (which represents the whole root pie) and the nested objects, which represent individual slices (plus subslices if there any). All properties mentioned below are optional, unless specified otherwise.

The root object has the following properties:

The following properties of the root object are used only in the case of partial loading, otherwise they are ignored:

For PieChart (but not FacetChart), if partial loading is used, at least two of afterSum, beforeSum and sum are mandatory. It doesn't matter which two of them are set, and you can also set all three, but the minimum is two. The omitted property will be calculated using the the ones that are set and the values in the subvalues array. If you set all three properties, the following equation must hold:

    beforeSum + afterSum + sumOfValues(subvalues) = sum

If this requirement is not met, the behavior of the chart is undefined.

The slice objects have the following properties:

Data sources

As all charts, Pie Chart and Facet Chart can load data in four different ways - preloaded data, data function, data URL and API calls. Data function and data URL are mutually exclusive. If both are specified, data function is used and data URL is ignored.

All loaded data is cached and is not requested again, unless reloadData() or replaceData() are called to clear the cache.

Preloaded data

Preloaded data is specified directly in the chart settings via the preloaded setting:

    new PieChart({
        data:{
            preloaded: {
                subvalues: [ ... ]
            }
        },
        ...
    });

This is useful if the data is small and you wish to avoid the overhead of dynamic loading, or if you wish to seed the chart with initial data that does not need to be dynamically loaded. Preloaded data will be always loaded, even if data function or data URL are specified alongside of it.

Data function

The data function has five parameters, in this order:

The callbacks can be called later (for example, in a response to an AJAX request), or they can be called immediately. The callbacks are only valid for a single call, and after one is called, the other may not be called.

Data URL

The data URL will have the same parameters appended to the URL as the data function - id, limit and offset. The data returned must be in JSON format.

API Calls

Both FacetChart and PieChart support several API calls that manipulate the data cache directly:

Partial loading

Normally data is requested for an entire pie, and new requests are issued when the user drills down to a previously unvisited level. However if you have a pie with very many slices (thousands), it may be impractial to download all that data at once. Instead PieChart and FacetChart support loading just a subset of all the slices in a single pie. As the user navigates the chart, more slices are requested as needed. To use this feature, several conditions must be met:

If all this is met, then the chart will attempt to request the data partially. Each request will thus have meaningful values for offset and limit parameters. The offset parameter specifies starting at which slice to return the result (the offset of first slice is 0), and the limit parameter specifies the minimum amount of slices to return. More slices than limit may be returned, but not less. The only exception is when the data source runs out of slices.

In the root object of the returned data there can also be offset and limit properties which describe the returned data.

The offset property, if set, must match the one that was passed in the request. If it is not set, then it is assumed to be 0. It is preferred that offset be the same as requested, however that is not mandatory. What matters is that the data returned overlaps with the data requested.

The limit property, if set, specifies the maximum number of elements in the subvalues array. It must not be set to a value smaller than the limit parameter in the request. If it is not set, of if it is set to a value which is larger than the count of elements in the subvalues array, then it is assumed that no further data is available.

It is possible for requests to return overlapping data intervals. In such cases, the later data overwrites the earlier data.

Partially loaded data is also possible if you load data via API calls. Simply add the offset and limit fields to the root data object which is passed to .addData(). In the case of PieChart, also add the afterSum / beforeSum / sum fields as described above. One additional concern is that the data cache only stores a contiguous range of slices. So when adding new data to the cache, care must be taken to ensure that it is adjacent to data which is already there. The very first data object however can be anywhere.

Partial loading and unstable data sources

A simple and intuitive way to implement partial loading would be to simply directly query a database with the specified parameters. For example, in a PHP/MySQL environment, this might result in a query similar to this:

select name, value from slices where parent_slice_id=$id limit ($offset, $limit);

This is indeed a very straightforward way of doing it, however it also carries with it a hidden danger. If the underlying data changes frequently (which is often the case for live data in a production environment), then this can result in problematic responses for the chart. Consider one such possible scenario:

Similarly, when deleting slices at the start, some slices might be omitted from the chart. And changing values for sum between requests will also result in incorrect slice size calculations.

There are several ways of avoiding these problems and others might be possible too, depending on your exact situation:

Partial loading is a powerful tool for efficiently handling big data, but implementing it correctly can be difficult. PieChart and FacetChart can withstand problematic data without catastrophic failure (crashing/infinite loops/etc), but the resulting visualization will almost certainly be incorrect.

Automatic categorization

This is an alternative data format for PieChart and FacetChart. This is incompatible with partial loading and in fact any kind of dynamic loading at all. That is, the data can be initially loaded via any of the standard methods (data function, data URL, preloaded data, API calls), but no subsequent requests for additional data will be made. All data must be loaded at the start. In addition, initial navigation cannot be specified either, since pies won't have a determined ID.

The upshot to this is that the data hierarchy will be built by PieChart/FacetChart itself. Instead of supplying hierarchial data with slices that have subslices that have subslices, you provide a flat list of data objects and a list of properties ("categories") by which to group them. For example:

new PieChart({
    data:{
        preloaded: {
            subvalues: [
                { make: "Volvo", year: 2014, name: "S60",    value: 10  },
                { make: "Volvo", year: 2015, name: "S60",    value: 20  },
                { make: "Volvo", year: 2016, name: "S60",    value: 30  },
                { make: "Volvo", year: 2014, name: "V70",    value: 40  },
                { make: "Volvo", year: 2015, name: "V70",    value: 50  },
                { make: "Volvo", year: 2016, name: "V70",    value: 60  },
                { make: "Volvo", year: 2014, name: "XC90",   value: 70  },
                { make: "Volvo", year: 2015, name: "XC90",   value: 80  },
                { make: "Volvo", year: 2016, name: "XC90",   value: 90  },
                { make: "VW",    year: 2014, name: "Golf",   value: 100 },
                { make: "VW",    year: 2015, name: "Golf",   value: 110 },
                { make: "VW",    year: 2016, name: "Golf",   value: 120 },
                { make: "VW",    year: 2014, name: "Passat", value: 130 },
                { make: "VW",    year: 2015, name: "Passat", value: 140 },
                { make: "VW",    year: 2016, name: "Passat", value: 150 },
                { make: "VW",    year: 2014, name: "Touran", value: 160 },
                { make: "VW",    year: 2015, name: "Touran", value: 170 },
                { make: "VW",    year: 2016, name: "Touran", value: 180 }
            ]
        },
        autoCategories: ["make", "year"]
    }
});

This produces a chart that has the make on the root drilldown level, then year on the second drilldown level, and model on the last drilldown level:

X