How to create charts and datamaps for data visualization

From Sense/Net Wiki
Jump to: navigation, search
  •  
  •  
  •  
  •  
  • 100%
  • 6.5.1
  • Enterprise
  • Community
  • Planned

Overview

A way to visualize your data stored in either the Sense/Net Content Repository or an external location, is using third party javascript libraries. For the purpose of this tutorial, we'll use chart.js and datamaps.js which are using d3.js. See our demo at the data-visualization demo-page after logging in as Admin.

Charts on Sense/Net demosite

Steps

1. Apply third-party libraries

Included javascript plugins are stored in the Content Repository: the necessary files will be found in the /Root/Global/scripts/dataviz folder:

  • Chart.min.js
  • Datamaps.all.js
  • d3.min.js
  • countries_hash.js
  • states_hash.js
  • Topojson.js


Use the custom css file from /Root/Global/styles:

  • SN.Dataviz.css

2. Include plugins in script files

Find the necessary SN.Chart.js in the folder: /Root/Global/scripts/sn. This is to initialize charts and create datasources. It includes the plugins at the first rows:

// using $skin/scripts/sn/SN.js
// using $skin/scripts/ODataManager.js
// using $skin/scripts/dataviz/Chart.min.js


Find SN.Util,js in the folder: /Root/Global/scripts/sn. It is expanded with the following sniplet, which allows SN solution to synchronize the used libraries for datamaps-visualization: First load a json file to be applied for datamaps:

// using $skin/scripts/dataviz/countries_hash.js


Then we added the neccessary functionality:

    // get country name after 2 or 3 letter country-code
    getCountryName: function (countryCode) {
        if (countryCode.length === 2) {
            for (var i = 0; i < ISOCountries.length; i++) {
                if (ISOCountries[i].alpha2 === countryCode)
                    return ISOCountries[i].name;
            }
        }
        else {
            for (var j = 0; j < ISOCountries.length; j++) {
                if (ISOCountries[j].alpha3 === countryCode)
                    return ISOCountries[j].name;
            }
        }
    },
    //get list of all country-name
    getAllCountries: function () {
        var countries = [];
        for (var obj in ISOCountries) {
            countries.push(ISOCountries[obj].name);
        }
        return countries;
    },
    //get list of all country-codes by num which determinates if code is 3 or 2 letter
    getAllCountryCodes: function (num) {
        var countryCodes = [];
        if (num === 2) {
            for (var obj in ISOCountries) {
                countryCodes.push(ISOCountries[obj].alpha2);
            }
        }
        else {
            for (var obj in ISOCountries) {
                countryCodes.push(ISOCountries[obj].alpha3);
            }
        }
        return countryCodes;
    },
    // get 2-or 3 letter countrycode by country name
    getCountryCode: function (countryName, num) {
        if (num === 2) {
            for (var i = 0; i < ISOCountries.length; i++) {
                if (ISOCountries[i].name === countryName)
                    return ISOCountries[i].alpha2;
            }
        }
        else {
            for (var j = 0; j < ISOCountries.length; j++) {
                if (ISOCountries[j].name === countryName)
                    return ISOCountries[j].alpha3;
            }
        }
    },
    // get 2 letter code by 3 letter code
    getAlpha2CountryCode: function (code) {
        for (var i = 0; i < ISOCountries.length; i++) {
            if (ISOCountries[i].alpha3 === code)
                return ISOCountries[i].alpha2;
        }
    },
   // get 3 letter code by 2 letter code
    getAlpha3CountryCode: function (code) {
        for (var i = 0; i < ISOCountries.length; i++) {
            if (ISOCountries[i].alpha2 === code)
                return ISOCountries[i].alpha3;
        }

3. Create polar-chart with chart.js

You can find as an example a custom Content View here: /Root/global/renderers/SalesWsExpectedRevenueChart.ascx. Its dataset structure is very similar to creating pie or doughnut chart. You will need to load the required plugins and stylesheets:

<sn:scriptrequest id="ScriptRequest1" runat="server" path="/Root/Global/scripts/sn/SN.Chart.js" />
<sn:cssrequest id="dataviz" runat="server" csspath="/Root/Global/styles/SN.Dataviz.css" />

Then define the markup for content:

<div class="ExpectedRevenuePieChart datavizContainer">
    <h1>Expected revenues</h1>
    <canvas id="ExpectedRevenuePieChart"></canvas>
</div>

Finally, include the javascript sniplet to display the chart itself on the canvas in the markup. Dataset is built on the result of an Odata request, and chart options are also set here:

  <script>
    var polarChartData;
    var polarChartDataRequest = $.ajax({
        url: odata.dataRoot + '/workspaces/Sales?$select=DisplayName,ExpectedRevenue,ChanceOfWinning&$top=5&$orderby=ExpectedRevenue desc&metadata=no&enableautofilters=true',
        dataType: "json",
        success: function (d) {
            polarChartData = d.d.results;
        }
    })
    var colors = ['#2c3e50', '#c0392b', '#f1c40f', '#2ecc71', '#7f8c8d'];
    var highlight = ['#34495e', '#e74c3c', '#f39c12', '#27ae60', '#95a5a6'];
    $.when(polarChartDataRequest).done(function () {
        var data = [];
        for (var i = 0; i < polarChartData.length; i++) {
            var item = {
                value: milRound(polarChartData[i].ExpectedRevenue),
                color: colors[i],
                highlight: highlight[i],
                label: trimLabel(polarChartData[i].DisplayName)
            }
            data.push(item);
        }
        var options = {};
        options.id = 'ExpectedRevenuePieChart';
        options.data = data;
        options.options = {
            responsive: true
        }
        var $polarChart = $('#ExpectedRevenuePieChart');
        $polarChart.width($polarChart.parent().width());
        SN.Charts.initPolarChart(options);
    });
    function createPercentage(d) {
        return d * 100;
    }
    function actualRevenue(percentage, expectedrevenue) {
        return percentage * expectedrevenue;
    }
    function trimLabel(d) {
        return d.replace(' Sales Workspace', '');
    }
    function milRound(x) {
        var data = String(x).length;
        if (data > 3) {
            var up_num = parseInt(data) - 3;
            var text = String(x);
            var test = text.substr(0, up_num);
            var zero = 'k';
            return test;
        }
    }
  </script>

4. Create bar-chart with chart.js

Bar-chart demo

You can find as an example a custom Content View here: /Root/global/renderers/SalesForecast.ascx. It's dataset structure is very similar to creating a line-chart. After the prequisite plugin-loading and markup definition, as shown in previous chapter above, include a javascript sniplet like this:

  <script>
        var chartData;
        var id = '<%=(this.Parent as Panel).Attributes["ClientID"]%>';
        var chartDataRequest = $.ajax({
            url: odata.dataRoot + '/workspaces/Sales?$select=DisplayName,Completion,ExpectedRevenue,ChanceOfWinning&$top=5&metadata=no&enableautofilters=true',
            dataType: "json",
            success: function (d) {
                chartData = d.d.results;
            }
        })
        $.when(chartDataRequest).done(function () {
            $.each(chartData, function (i, item) {
                var $revenueLabels = $("<div class='columns large-2 small-2 revenueLabel'>" + SN.Util.formatNumber(item.ExpectedRevenue) + " $</div>").appendTo($('.revenueLabels'));
                var $chanceLabels = $("<div class='columns large-2 small-2 chanceLabel'>" + (item.ChanceOfWinning) * 100 + " %</div>").appendTo($('.chanceLabels'));
                var states = $("<div class='columns large-2 small-2 state'><h5>" + trimLabel(item.DisplayName) + "</h5></div>").appendTo($('.forecaststates'));
            });
            var options = {};
            options.id = 'salesForecast' + id;
            options.data = chartData;
            options.label = ['DisplayName', trimLabel];
            options.tooltipLabel = 'Forecast';
            options.keys = [{ keys: ['ChanceOfWinning', 'ExpectedRevenue'], method: forecast }];
            options.colors = [
                {
                    fillColor: "#f1c40f",
                    highlightFill: "#f39c12",
                }
            ];
            options.options = {
                showScale: false,
                scaleGridLineWidth: 0,
                scaleShowHorizontalLines: false,
                scaleShowVerticalLines: false,
                scaleShowGridLines: false,
                scaleShowLabels: false,
                scaleSteps: null,
                barShowStroke: false,
                barValueSpacing: 20
            }
            SN.Charts.initBarChart(options);
        });
        function trimLabel(d) {
            return d.replace(' Sales Workspace', '');
        }
        function forecast(chanceofwin, expectedrevenue) {
            return chanceofwin * expectedrevenue;
        }
  </script>

5. Create a datamap

An example for custom view for datamaps-chart is included to Sense/Net (/Root/global/renderers/SalesWorkspaceUsaMap.ascx). As previously, at the top of the ascx-file invite necessary plugins, included in the Content Repository:

<sn:scriptrequest id="d3" runat="server" path="/Root/Global/scripts/dataviz/d3.min.js" />
<sn:scriptrequest id="topojson" runat="server" path="/Root/Global/scripts/dataviz/topojson.js" />
<sn:scriptrequest id="ScriptRequest2" runat="server" path="/Root/Global/scripts/dataviz/datamaps.all.js" />
<sn:scriptrequest id="ScriptRequest1" runat="server" path="/Root/Global/scripts/sn/SN.Chart.js" />
<sn:scriptrequest id="uStates" runat="server" path="/Root/Global/scripts/dataviz/states_hash.js" />
<sn:cssrequest id="dataviz" runat="server" csspath="/Root/Global/styles/SN.Dataviz.css" />


After having the markup defined, paste the javascript sniplet to the view:

  <script>
    var usaSalesMap = {};
    var salesWsData = [];
    var salesWorkspaces = $.ajax({
        url: odata.dataRoot + '/workspaces/Sales?$select=DisplayName,ExpectedRevenue,ChanceOfWinning&metadata=no&enableautofilters=true',
        success: function (d) {
            salesWsData = d.d.results;
        }
    });
    $.when(salesWorkspaces).done(function () {
        for (var key in states) {
            usaSalesMap[key] = { "name": states[key] };
        };
        for (var i = 0; i < salesWsData.length; i++) {
            for (var state in usaSalesMap) {
                if (salesWsData[i].DisplayName.indexOf(usaSalesMap[state].name) > -1) {
                    usaSalesMap[state].fillKey = 'Highlighted';
                    usaSalesMap[state].ExpectedRevenue = salesWsData[i].ExpectedRevenue;
                    usaSalesMap[state].ChanceOfWinning = salesWsData[i].ChanceOfWinning;
                }
            }
        }
        var options = {
            scope: 'usa',
            id: 'usaMapContainer',
            highlightFillColor: '#34495e',
            popupLabel: 'Expected revenue',
            popupValue: 'ExpectedRevenue',
            popupFormat: popupFormatUsa,
            highlightBorderColor: '#2c3e50',
            highlightBorderWidth: 3,
            fills: {
                'Highlighted': '#1abc9c',
                defaultFill: '#D2D7D3',
            },
            data: usaSalesMap
        };
        var $usaMap = $('#usaMapContainer');
        $usaMap.height($usaMap.parent().width() * 0.5);
        SN.Charts.initMap(options);
    });
    function popupFormatUsa(d, data) {
        var num = 0, chance = 0;
        if (typeof d !== 'undefined')
            num = SN.Util.formatNumber(d);
        if (typeof data.ChanceOfWinning !== 'undefined')
            chance = createPercentage(data.ChanceOfWinning);
        return '<b>' + data.name + '</b></br>Expected revenue: ' + num + ' $</br>Chance to sell: ' + chance + '%';
    }
    function createPercentage(d) {
        return d * 100;
    }
  </script>

6. Portlet usage

Apply User control Portlet on a Portlet Page and add custom views defined previously to display charts or datamaps.

Related links


References