Skip to content
This repository has been archived by the owner on May 4, 2019. It is now read-only.

Tutorial

Adil edited this page Nov 12, 2013 · 32 revisions

Let's create a Keshif interface for a common and interesting dataset: Nobel Prizes! First comes the data, as with any visualization or browser. It is here in Google Docs, with public read access.

To start coding, let's create the barebone HTML page that will hold our interface (you can structure this any way you like, embed it in custom pages, blog posts, etc...)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
   <title>Nobel Prize Winners</title>
   <script type="text/javascript" src="http://www.google.com/jsapi"></script>
   <script type="text/javascript" src="../jquery/jquery-1.9.1.min.js"></script>
   <script type="text/javascript" src="../d3.v3/d3.v3.js" charset="utf-8"></script>
   <script type="text/javascript" src="../keshif.js" charset="utf-8"></script>
   <link rel="stylesheet" href="../keshif.css"/ type="text/css">
   <script type="text/javascript" src="moment.min.js" charset="utf-8"></script>
</head>
<body><div id="nobelDiv"></div></body>
</html>

Notice the scripts we included, mainly Google API, jquery, d3, and finally keshif, all required any time ou want to use keshif library. We also linked keshif css so we have the basic interface styles needed, and moment library so that we can later manipulate date data easier. The body includes just a single empty div, with a given id "nobelDiv".

Now, let's bring our data to this page & create the keshif browser, simply by adding a few lines of javascript:

function loadChart() {
    kshf.init({
        chartTitle: "Nobel Prize Winners",
        domID : "#nobelDiv",
        dirRoot: "../",
        categoryTextWidth: 200,
        source : {
            gdocId : '0Ai6LdDWgaqgNdDNVcXlscjl4RzRZNl9ZSkNJLU1DWVE',
            sheets : [ {name:"Prizes"}, {name:"Laureates"} ]
        },
        columnsSkip : ["Overall Motivation","laureate_id"],
        list: {
            sortColWidth: 45,
            sortOpts : [ {name: 'Year'} ],
            contentFunc : function(d) {
                var laureateID = d.data[kshf.dt_ColNames.Prizes.laureate_id];
                var laureate = kshf.dt_id.Laureates[laureateID];
                return "<div class=\\"iteminfo iteminfo_0\\">" +
                    laureate.data[kshf.dt_ColNames.Laureates.firstname] + " " + 
                    laureate.data[kshf.dt_ColNames.Laureates.surname] +
                    "</div>";
            }
        }
    });
}
$(document).ready(loadChart);

We used jquery's $(document).ready callback to load the data & create the chart. There's only one call to kshf.init to create our interface. Let's look at the parameters:

  • chartTitle is used as the top chart title, and is actually optional.
  • domID is where you want keshif to insert the interface. Important!
  • dirRoot is where your put the keshif source directory relative to the current directory. By default, it's expected to be in the same folder, but your case may vary, and this sample puts keshif in the parent directory.
  • categoryTextWidth is how wide you want the left panel width to be (in pixels), which hosts category facets. You can set the value around 150, and modify it as your category labels require.
  • source defines our datasource, and is mostly self explanatory, and configurable further as noted here in API docs. Note that our document is identified by 0Ai6LdDWgaqgNdDNVcXlscjl4RzRZNl9ZSkNJLU1DWVE , available as a part of the link. While this document has 3 sheets, we are interested in two of them, Prizes and Laureates.
  • By default, Keshif will try to create a search facet for all columns in the first table you specified. We don't want to create search on 2 of the columns, so we list them in columnsSkip.
  • Finally, we set our list display options, which includes sortColWidth, sortOpts, and contentFunc. Details of config options are here in API docs.
  • sortOpts is a list of sorting options. The simplest approach is to use the column names as sorting options, yet customizations are possible. You need to include at least one option here.
  • contentFunc is used here to first retrieve the laureateID from prize columns, with the correct integer index retrieved using kshf.dt_ColNames.Prizes.laureate_id. Then, this laureateID is used to look up the second table, as in kshf.dt_id.Laureates[laureateID], which includes the data for this item (row) under .data member.

To finish our initial take, let's also make sure that the div that holds keshif interface is of correct size, and further dynamic to browser screen size! We create a function called resetSize below and call it in load chart function before initializing keshif, and then also link this function to window resize events and call kshf.updateLayout() after resize events so that keshif library can re-evalute its layout with updated div size. You can, if you'd like, define a static size on div element (using css or other styling options), and remove this dynamic behavior.

function resetSize(){
    $('#nobelDiv').height($(window).height()-10);
    $('#nobelDiv').width (1000);
}
function loadChart() {
    resetSize();
    $(window).resize(function() {
        resetSize();
        kshf.updateLayout();
    });
    // keshif init function is here as usual.
}

Let's take a look at the result of our simple approach so far: Nobel dataset - tutorial results

But, we can improve this result further, to the point of our final demo featured here: Nobel dataset - final results

Let's look at the differences on how we've made this initial result more useful. Most importantly, we will define charts parameter (an array), and remove columnsSkip option since we no longer will let keshif auto generate the facets.

Adding TimeChart

Since none of our columns included a complete date informations (years were processed as integers) keshif didn't know to apply them to the first category facet. Let's define this mix of category & time filter as first facet:

{
    facetTitle: "Category",
    timeTitle: "Date",
    timeItemMap : function(prize){ return new Date(moment(prize.data[prizeCols['Year']]+"01-01","YYYY-MM-DD")); },
    singleSelect: true,
    textFilter: 'about'
}

We needed to set timeItemMap function, so that it can convert the 'Year' Column to the date, and parse it using moment library, and return a javascript Date object. We also enabled singleSelect option, and changed the text that would be shown upon filtering, to "about XYZ**, where XYZ would be the selected category.

Filtering by columns in other table

Before, we had removed some unnecessary fields from automatically appearing in the filter. Now, we want to insert columns in another sheet for filtering. First, let's add filter for countries the prize winners were born in.

{
    facetTitle: "Born Country",
    catItemMap : function(prize){ 
        var laureate = kshf.dt_id.Laureates[prize.data[prizeCols.laureate_id]];
        return laureate.data[kshf.dt_ColNames.Laureates.bornCountry];
    },
    catLabelText: function(d) { return getGender(d.data[1]); }
}

We defined "Gender" as the facetTitle, the catItemMap function gets a prize (row from main table), access the laureate id column, and then use kshf.dt_id.Laureates table (where Laureates is the sheet name) to retrieve the laureate information, and finally return its data indexed at kshf.dt_ColNames.Laureates.bornCountry.

We then repeat this approach in creating gender and affiliation filters:

{
   facetTitle: "Gender",
   catItemMap : function(prize){ 
       var laureate = kshf.dt_id.Laureates[prize.data[prizeCols.laureate_id]];
       return laureate.data[lautCols.gender];
   },
   catLabelText: function(d) { return getGender(d.data[1]); }
},{
    facetTitle: "Affiliation",
    catItemMap : function(prize){ 
        var laureate = kshf.dt_id.Laureates[prize.data[prizeCols.laureate_id]];
        return laureate.data[lautCols.name];
    }
}

Adding text search for list view

We added the following options to list config:

textSearch : "names",
textSearchFunc : function (d) {
    var laureate = kshf.dt_id.Laureates[d.data[prizeCols.laureate_id]];
    return laureate.data[lautCols.firstname] + " " + laureate.data[lautCols.surname];
},

Improving item display in the list view

We modify the contentFunc to include photos, and more information per item, such as categories, birth date and place, motivations for the awards, etc

contentFunc : function(d) {
    var j;
    var str="";
    var laureate = kshf.dt_id.Laureates[d.data[prizeCols.laureate_id]];
    var surname_short = laureate.data[lautCols.surname].toLowerCase().replace(/ /g,"_");
    surname_short = surname_short.replace("von_","").replace("la_","");
    var imgUrl="./nobel_photo/"+surname_short+".jpg";

    if(laureate.data[lautCols.gender]!=='org'){
        str+="<img src=\\""+imgUrl+"\\" width=\\"80\\" style=\\"float:left\\">";
        str+="<div style=\\"position: absolute; left:85px;\\">";
    } else {
        str+="<div style=\\"position: relative; left:85px;\\">";
    }

    // description
    str+="<div class=\\"iteminfo iteminfo_0\\">";
    str+=laureate.data[lautCols.firstname] + " " + laureate.data[lautCols.surname];
    str+="</div>";

    str+="<div class=\\"iteminfo iteminfo_3\\">";
    if(laureate.data[lautCols.gender]!=='org'){
        var born=laureate.data[lautCols.born];
        var died=laureate.data[lautCols.died];
        str+="Born: "+laureate.data[lautCols.bornCountry]+", "+laureate.data[lautCols.bornCity]+
            (born!==null?(", "+moment(born).format("MMM. DD YYYY")):"")+
            (died!==null?(", Died in "+moment(died).format("MMM. DD YYYY")):"")
            ;
    }
    str+="</div>";

    str+="<div class=\\"iteminfo iteminfo_1\\">Nobel Prize In: ";
    str+=d.data[prizeCols['Category']];
    str+="</div>";

    str+="<div class=\\"iteminfo iteminfo_2\\">Motivation: ";
    str+=d.data[prizeCols['Motivation']];
    str+="</div>";

    var mot = d.data[prizeCols['Overall Motivation']];
    if(mot!=="" && mot!== null){
        str+="<div class=\\"iteminfo iteminfo_3\\">Overall motivation: ";
        str+=mot;
        str+="</div>";
    }

    str+="</div>"

    return str;
}
Clone this wiki locally