How to create a Network Chart visualization for TIBCO Spotfire® using JSViz and ZoomCharts
Last updated:
9:19am Dec 12, 2018

 


Introduction

ZoomCharts is a JavaScript/HTML5 library that lets you add visually rich and interactive charts to TIBCO Spotfire® through the JSViz Framework - a custom extension for Spotfire® that allows users create their own visualizations using JavaScript libraries and seamlessly integrate them with the TIBCO Spotfire® platform. 

With a few simple steps you can access more than 20 advanced chart types from ZoomCharts JavaScript library, including expandable network graphs, area charts and multi level pie/donut charts. ZoomCharts core strengths are the high level of built-in interactivity and speed with Big Data, and in this article I will walk you through the process of taking their Network Chart sample and bringing it into TIBCO Spotfire® using JSViz.


Step #1: Setup a new default JSViz

As a first step with any new chart type I just take the entire sample, including the sample data, and bring it into JSViz.

So I open up Spotfire with a dummy data file, in my case the StoreSales.sbdf that ships with JSViz, and insert a JSViz visualization.

Now I need to add the JSViz JavaScript and the ZoomCharts JavaScript files to the JSViz library.  So I add the files from the JSViz download as though I was following the JSViz tutorial:

  • jQuery.js
  • JSViz.js
  • Template.css
  • Template.js

Now I add the files to the Content list, jQuery first, then the Template files, then lastly JSViz.js:

At this point, the default visualization appears:

Now I can change the name of the Template.css and Template.js files to reflect my new visualization and save the DXP:


Step #2: Bring in the entire Sample Code

The next step in setting up the network chart is to take the entire source code for the sample, including the sample data, and set it up in JSViz.

First I add the CDN reference to the ZoomCharts JavaScript library to the JSViz library and add it to the Content list:

      

Now we take the "data" variable in the jsfiddle example and past it at the top of our ZoomNetworkChart.js file:

//////////////////////////////////////////////////////////////////////////////
// #region Drawing Code
//

var data = {
    "nodes":[
   ...
    ]
};

//
// Main Drawing Method
//

function renderCore(sfdata)
{

and we take the chart creation code and paste it into the renderCore() method where the placeholder text indicates, replacing the call to displayWelcomeMessage:

    //
    // Replace the following code with actual Visualization code
    // This code just displays a summary of the data passed in to renderCore
    //

    var chart = new NetChart({
        container: document.getElementById("demo"),
        area: { height: null },
        data: { preloaded: data },
  ...

The only change we have to make at this point is to insert the chart into the "js_chart" DIV element that is provided by JSViz.  So we change the single line of code:

...
        container: document.getElementById("js_chart"),
...

and after saving the file, the network chart appears:


Step #3: Setup Spotfire Data Tables

The chart seems to want two separate sets of data:

  • Nodes
  • Links

So I am going to set these up as two separate tables in Spotfire and pass them both to the visualization. So I take the JSON data and create two delimited data files to load into Spotfire:

Nodes.txt:
\! filetype=Spotfire.DataFormat.Text; version=2.0;
\! property=Name; category=Column; type=String;
\! property=DataType; category=Column; type=String;
id;age;name;loaded;className;
String;Int;String;Bool;String;
"m-1";20;"Joe";true;"a";
"m-2";15;"Fred";true;"a";
"m-3";16;"Tom";true;"a";
...

Links.txt:
\! filetype=Spotfire.DataFormat.Text; version=2.0;
\! property=Name; category=Column; type=String;
\! property=DataType; category=Column; type=String;
id;from;to;type;
String;String;String;String;
"l01";"m-1";"f-1";"friend";
"l02";"m-1";"f-2";"friend";
"l03";"m-1";"f-3";"friend";
...

Once I have imported these text files into Spotfire, I add them into JSViz, passing all column values as we want un-aggregated data:

    

    

Now, all that remains is to remove the dummy StoreSales table:

 


Step #4: Re-create the chart JSON object

Now that we have the chart data in Spotfire and being passed into our JS code by JSViz, we can recreate the formatted JSON object that the Zoom Chart needs to render itself

First we create a new variable that will convert our formatted chartdata:

//
// Main Drawing Method
//

var networkdata = { nodes : [], links : [] };

...

Then we use a JavaScript forEach() function to iterate over the Nodes data table items and create an entry in the Nodes array for each row of data.  This code is inserted immediately before the call to create the chart object.

    //
    // Format the Nodes entries from the sfdata.data array
    //
    networkdata.nodes = [];

    sfdata.data.forEach ( function ( item, index )
    {
        var node = { "id"  : item.items[0],
                     "age" : item.items[1],
                    "name" : item.items[2],
                  "loaded" : item.items[3],
               "className" : item.items[4] };

        networkdata.nodes.push ( node );
    });

Then we repeat this process for the Links data table items.  These can be found in the sfdata.additionalDataTables array:

    //
    // Format the Links entries from the sfdata.additionalTables[0].data array
    //
    networkdata.links = [];

    sfdata.additionalTables[0].data.forEach ( function ( item, index )
    {
        var link = { "id"  : item.items[0],
                    "from" : item.items[1],
                      "to" : item.items[2],
                    "type" : item.items[3] };

        networkdata.links.push ( link );
    });

Now we are ready to switch the chart to use our new chartdata object:

    var chart = new NetChart({
        container: document.getElementById("js_chart"),
        area: { height: null },
        data: { preloaded: networkdata },
...

At this point our network chart will redraw and look the same as before.  However, if you change the Filter settings, the chart will redraw to reflect the changes.

 


Step #5: Tidy Up

As with every JS Chart Library, there will be some tidying up to turn on/off different features.  In my example I don't need the legend so I look in the API Documentation and disable it:

...
    legend: {enabled: false}
...

Also I don't want the Controls to allow zoom in and out and toggle fullscreen etc so I disable these as follows:

...
    toolbar: {enabled: false}
...

 


Step #6: Resizing

Implementing resizing means that we will need to call our chart drawing code from both the main renderCore() function and the windows.onresize() function.  So we will need to wrap the drawing code in a function that can be called from either place.  The network data is available as a global so the only parameters we will need to pass in are the required height and width which we use to set the size of the network chart:

function drawNetworkChart ( height, width )
{
    var chart = new NetChart({
        container: document.getElementById("js_chart"),
        area: { height: height, width: width },
...

Now we have to modify the main renderCore() code to call this function:

...

    drawNetworkChart ( height, width );

    wait ( sfdata.wait, sfdata.static );
}

and add the call to drawNetworkChart to the window.onresize() function:

window.onresize = function (event) {
    resizing = true;
    if ($("#js_chart")) 
    {
        var width = window.innerWidth;
        var height = window.innerHeight;

        drawNetworkChart ( height, width );
    }
    resizing = false;
}

 


Step #7: Marking

In this example we will only implement marking from the Network Chart back to Spotfire.  In order to do that we must first store the marking indices passed in to renderCore() inside the network chart elements, then we must respond to click events on the network chart and pass those same marking indices back to Spotfire.

ZoomCharts provide an element in their data schema called "extra" to allow the storing of data values in the actual chart elements.  We will store the Marking Index for each item in a variable called "mid" (short for marking id), so we just need to add an extra line to the forEach() statements we created earlier:

    networkdata.nodes = [];

    sfdata.data.forEach ( function ( item, index )
    {
        var node = { "id"  : item.items[0],
                     "age" : item.items[1],
                    "name" : item.items[2],
                  "loaded" : item.items[3],
               "className" : item.items[4],
                   "extra" : { "mid": item.hints.index } };

        networkdata.nodes.push ( node );
    });

Next we add an event handler to our chart setup to indicate that a function called selectionEvent() is to be called whenever the user clicks on a Node in the chart:

    var chart = new NetChart({
        container: document.getElementById("js_chart"),
        area: { height: height, width: width },
        data: { preloaded: networkdata },
...
        legend: {enabled: false},
        toolbar: {enabled: false},
        events: {onSelectionChange: selectionEvent}
    });

Lastly we need our selectionEvent() function to loop through all the selected Nodes and send their marking IDs to Spotfire:

function selectionEvent ( event )
{
    var indicesToMark = [];
    var markData = {};
    markData.markMode = "Replace";
    markData.indexSet = indicesToMark;

    var selection = event.selection;

    for ( var i = 0 ; i < selection.length ; i ++ )
    {
        var item = selection[i];

        if ( item.isNode )
        {
            indicesToMark.push ( item.data.extra.mid );
        }
    }

    markIndices ( markData );
}

Now, whenever we click on a Node, that Node will be marked in the Nodes table:


Summary

In this article we walked through the standard process for creating JSViz visualization using a 3rd party visualization library, in this case from Zoom Charts.  The attached DXP file contains the final result.

The steps involved will be the same for other chart types from Zoom Charts.  I will be posting example for Area Charts and Doughnut Charts shortly.

Attachments

AttachmentSize
Binary Data zoomnetworkchart.dxp392.32 KB

Feedback (1)

We are using Spotfire 7.11 and the JSVis Framework jsviz_3.4.0.12_03-06-2018

Is there anyway to replicate this network chart example in 7.11?  Also when opened in our environment the 'zoomnetworkchart' can you identify the code that continues to run in the background - a spinning wheel keeps spinning?

When I open the attached zoomnetworkchart.dxp in our 7.11 library the report loads but displays a warning message: 

"This analysis has been saved in version 7.12 of Spotfire and may contain information that this version of Spotfire cannot display".
I clear the message and save the analysis file

But then get this message "Browser disposal timed out.
   at Spotfire.Dxp.Cef.CefOffScreenBrowser.Dispose()
   at Spotfire.Dxp.Forms.CefBrowserControl.CefHtmlRenderer.Dispose()
   at Spotfire.Dxp.Application.Extension.CustomVisual.RenderCore(RenderArgs renderArgs)
   at SpotfirePS.Framework.JSVisualization.Core.JSVisualizationModel.RenderCore(RenderArgs renderArgs)"

So believing it may be a compatability issue I remove from used content the jQuery and JSVis javascript library files and replace with the versions found in the jsviz_3.4.0.12_03-06-2018 folder.

This time i receive one warning message

"Type: TypeError
Message: Right-hand side of 'instanceof' is not an object
Stacktrace: TypeError: Right-hand side of 'instanceof' is not an object
    at Function.t.isDomObject (https://cdn.zoomcharts-cloud.com/1/18/4/zoomcharts.js:32:2734)
    at Function.t.ensurePropertyType (https://cdn.zoomcharts-cloud.com/1/18/4/zoomcharts.js:37:22577)
    at Function.t.copyValue (https://cdn.zoomcharts-cloud.com/1/18/4/zoomcharts.js:37:20933)
    at Function.t.updateRecursive (https://cdn.zoomcharts-cloud.com/1/18/4/zoomcharts.js:37:19496)
    at e.t.apply (https://cdn.zoomcharts-cloud.com/1/18/4/zoomcharts.js:37:27211)
    at e.apply (https://cdn.zoomcharts-cloud.com/1/18/4/zoomcharts.js:42:27726)
    at e.apply (https://cdn.zoomcharts-cloud.com/1/18/4/zoomcharts.js:44:31191)
    at new e (https://cdn.zoomcharts-cloud.com/1/18/4/zoomcharts.js:44:30806)
    at new s (https://cdn.zoomcharts-cloud.com/1/18/4/zoomcharts.js:48:3829)
    at new e (https://cdn.zoomcharts-cloud.com/1/18/4/zoomcharts.js:60:25467)"

ndicocco 10:08am Jun. 27, 2018