Sunday, June 5, 2011

Simple Treemap


var jsondata = {
    row1 : {
      data1 : 10,
      data2 : 10,
    },
    row2 : {
      data3 : 20,
      data4 : 40,
    } 
};

var pvdom = pv.dom(jsondata);

//recursive functions
function title(d) {
  return d.parentNode ? (title(d.parentNode) + "." + d.nodeName) : d.nodeName;
}

var re = "",
    color = pv.Colors.category19().by(function(d) d.parentNode.nodeName),
    datanodes = pvdom.root("hanya-nama").nodes(); //change it to array

var vis = new pv.Panel()
    .width(400)
    .height(400);

var treemap = vis.add(pv.Layout.Treemap)
    .nodes(datanodes)
    .round(true);
 
treemap.leaf.add(pv.Panel)
  .fillStyle(function(d) color(d).alpha(title(d).match(re) ? 1 : .2))
  .strokeStyle("#fff")
  .lineWidth(1)
  .antialias(false);


treemap.label.add(pv.Label)
    .textStyle(function(d) pv.rgb(0, 0, 0, title(d).match(re) ? 1 : .2));

vis.render();

Reference :

JSON to DOM operator

JSON - as explained in protovis documentation - is a hierarchical Javascript object (map). And since some of the Provotis visualization need dom object instead of map, we then have pv.dom function exist to cover this transformation.

This article will show you not the actual usage of the function. But just show you what JSON object before and after transformed into using pv.dom function in Firebug's DOM console.

The JSON declaration code shown below.
var jsondata = {
    row1 : {
      data1 : 10,
      data2 : 20,
    },
    row2 : {
      data3 : 30,
      data4 : 40,
    } 
};

Firebug's DOM view on jsondata variable.

"jsondata" object map

Now let's continue our code by adding these lines. We will have a converted JSON object into array using nodes() function of a constructed DOM operator base on jsondata.
var pvdom = pv.dom(jsondata);
var jsonarray = pvdom.nodes();
Here we now have a pvdom object and a related multi dimensional arrays - jsonarray.

Let's see the internal of each object in Firebug.




As you can see, jsonarray is a converted array from object jsondata through pvdom DOM operator. If you look at the array you can see that each node is represented by an index (root = 0, row1=1, row2=4, data1=2, data2=3, data3=5, data4=6).

Now, with no further explanation I hope you are clearer on the usage of pv.dom operator to help you get started with it.

References

Saturday, May 28, 2011

Protovis 2 Layer Wedges

This is my solution sample for question at Protovis's group question on wedge.

Hope it helps much on your adventure with Protovis !

Two Layers Wedge - Outer Laye'r Wedge are Divided Portions of ARelated Inner Layer Wedge


  1 var datacompound = [ 
  2 //Layer 1 
  3 [12.0212.0193], 
  4 //Layer 2 
  5
  6 [147], 
  7 [651], 
  8 [23], 
  9 [444
 10
 11 ]; 
 12  //Preprocessed Raw Data 
 13 var c = pv.Scale.linear(00.20.40.60.81).range("gold""olivedrab"
 14 "violet""#21004F""darkseagreen""lightseagreen"); 
 15 datacompound[0] = pv.normalize(datacompound[0]); 
 16 for(i=0; i<datacompound[1].length; i++) 
 17
 18 datacompound[1][i] = pv.normalize(datacompound[1][i]); 
 19 for(j=0;j<datacompound[1][i].length;j++) 
 20 datacompound[1][i][j] = datacompound[0][i]*datacompound[1][i][j]; 
 21
 22
 23
 24 datacompound[1] = pv.blend(datacompound[1]); 
 25 var sum1 = 0, sum2 = 0, offset1 = 0, offset2 = 0
 26 var startAngle = 0*Math.PI/180
 27  //Panel 
 28 vis = new pv.Panel() 
 29 .width(600
 30 .height(600
 31 .left(300
 32 .top(300
 33  //First Layer 
 34 .add(pv.Wedge) 
 35 .data(datacompound[0]) 
 36 .innerRadius(135
 37 .outerRadius(195
 38 .startAngle(function() {if(this.index>0
 39 offset1+=datacompound[0][this.index-1]*2*Math.PI; else offset1=startAngle; 
 40 return offset1;}) 
 41 .angle(function(d) d*2*Math.PI) 
 42 .fillStyle(function(d) {if(this.index>0
 43 sum1+=datacompound[0][this.index-1]; else sum1=0return c(sum1);}) 
 44 .strokeStyle("white"
 45 .lineWidth(4
 46  //Second Layer 
 47 .add(pv.Wedge) 
 48 .data(datacompound[1]) 
 49 .innerRadius(200
 50 .outerRadius(275
 51 .startAngle(function() {if(this.index>0
 52 offset1+=datacompound[1][this.index-1]*2*Math.PI; else offset1=startAngle; 
 53 return offset1;}) 
 54 .angle(function(d) d*2*Math.PI) 
 55 .fillStyle(function(d) {if(this.index>0
 56 sum1+=datacompound[1][this.index-1]; else sum1=0return c(sum1);}) 
 57 .strokeStyle("white"
 58 .lineWidth(4
 59 .root.render(); 

Wednesday, May 25, 2011

pv.scale.Ordinal & splitBanded function


pv.scale.Ordinal represents an ordinal scale. An ordinal scale represents a pairwise mapping from n discrete values in the input domain to n discrete values in the output range.

pv.Scale.ordinal generate a pair mapping function from input domain to output range
splitBanded(min, max, band) function

Sets the range from the given continuous interval. The interval [min, max] is subdivided into n equispaced bands, where n is the number of (unique) values in the domain. The first and last band are offset from the edge of the range by the distance between bands.

The band width argument, band, is typically in the range [0, 1] and defaults to 1. This fraction corresponds to the amount of space in the range to allocate to the bands, as opposed to padding. A value of 0.5 means that the band width will be equal to the padding width. The computed absolute band width can be retrieved from the range as scale.range().band.

If the band width argument is negative, this method will allocate bands of a fixed width -band, rather than a relative fraction of the available space.

Tip: to inset the bands by a fixed amount p, specify a minimum value of min + p (or simply p, if min is 0). Then set the mark width to scale.range().band - p.

This method must be called after the domain is set.


pv.Scale.ordinal("ABCDE".split("")).splitBanded(0, 500, 0.8)


Sample Code :

<html>
<head>
<script type="text/javascript"  src="protovis-r3.2.js"></script>
<title>pv.Scale.ordinal</title>
</head>
<body>
<div id="pesan">
</div>
<script type="text/javascript+protovis"
    var ordinalidx = "ABCDE".split("");
    var x = pv.Scale.ordinal(ordinalidx).splitBanded(05000.8);

    document.getElementById("pesan").innerHTML 
        += "ordinalidx = " + ordinalidx + "<BR />";
    document.getElementById("pesan").innerHTML 
        += "band width = " + x.range().band + "<BR />";

    document.getElementById("pesan").innerHTML 
        += "x['A'] = " + x('A') + "<BR />";
    document.getElementById("pesan").innerHTML 
        += "x['B'] = " + x('B') + "<BR />";
    document.getElementById("pesan").innerHTML 
        += "x['C'] = " + x('C') + "<BR />";
    document.getElementById("pesan").innerHTML 
        += "x['D'] = " + x('D') + "<BR />";
    document.getElementById("pesan").innerHTML 
        += "x['E'] = " + x('E') + "<BR />";
    /*
    */

</script>
</body>
</html>


Result :

ordinalidx = A,B,C,D,E
band width = 88.23529411764707
x['A'] = 9.80392156862745
x['B'] = 107.84313725490196
x['C'] = 205.8823529411765
x['D'] = 303.921568627451
x['E'] = 401.9607843137255

pv.scale.Ordinal & split function


pv.scale.Ordinal represents an ordinal scale. An ordinal scale represents a pairwise mapping from n discrete values in the input domain to n discrete values in the output range.

pv.Scale.ordinal generate a pair mapping function from input domain to output range
split(min, max) function

Sets the range from the given continuous interval. The interval [min, max] is subdivided into n equispaced points, where n is the number of (unique) values in the domain. The first and last point are offset from the edge of the range by half the distance between points.

This method must be called after the domain is set.

pv.Scale.ordinal("ABCDE".split("")).split(0, 500)


Sample Code :

<html>
<head>
<script type="text/javascript"  src="protovis-r3.2.js"></script>
<title>pv.Scale.ordinal</title>
</head>
<body>
<div id="pesan">
</div>
<script type="text/javascript+protovis"
    var ordinalidx = "ABCDE".split("");
    var x = pv.Scale.ordinal(ordinalidx).split(0500);

    document.getElementById("pesan").innerHTML 
        += "ordinalidx = " + ordinalidx + "<BR />";
    document.getElementById("pesan").innerHTML 
        += "x['A'] = " + x('A') + "<BR />";
    document.getElementById("pesan").innerHTML 
        += "x['B'] = " + x('B') + "<BR />";
    document.getElementById("pesan").innerHTML 
        += "x['C'] = " + x('C') + "<BR />";
    document.getElementById("pesan").innerHTML 
        += "x['D'] = " + x('D') + "<BR />";
    document.getElementById("pesan").innerHTML 
        += "x['E'] = " + x('E') + "<BR />";
</script>
</body>
</html>


Result :

ordinalidx = A,B,C,D,E
x['A'] = 50
x['B'] = 150
x['C'] = 250
x['D'] = 350
x['E'] = 450


Array map() Function

Code

<html>
    <head>
    <script type="text/javascript"  src="jquery-1.4.4.min.js"></script>
    <script type="text/javascript"  src="protovis-r3.2.js"></script>
    <title>Array Map Function</title>
    </head>
    <body>
    <div id="pesan1">
    </div>
    <script type="text/javascript">
    var data = [0,1,3,5];    
    
    data = data.map(function(x) 
        {
            return 2*x;
        }
    );
    
    document.getElementById("pesan1").innerHTML = data;
    </script>

    </body>
</html>


Result :

0,2,6,10

pv.Scale.Linear (Excel Simulated Data)

To make myself understand how actually pv.Scale.linear does, I'd done some simulation in Excel spreadsheet. May it can help you as well...

First, I simulate using domain 0 to 200 and data range from 0,10. The simulated f(x) will look like pic below

pv.Scale.linear simulation for domain = [0,200] and range = [0,10]
pv.Scale.linear syntax related to domain and data range is :

pv.Scale.linear(d0, d1).range(r0, r1);

And this code show how the pv.Scale.linear construct do...


<html>
    <head>
    <script type="text/javascript"  src="protovis-r3.2.js"></script>
    <title>PV Scale Linear</title>
    </head>
    <body>
    <div id="pesan">
    </div>
    <script type="text/javascript+protovis"
        var y = pv.Scale.linear(0200).range(010);
        document.getElementById("pesan").innerHTML += "y.ticks() = " + y.ticks() + "<BR />";
        for(i=0;i<=17;i++)
            document.getElementById("pesan").innerHTML += "y(" + i + ") = " + y(i).toFixed(2) + "<BR />";
    </script>
    </body>
</html>


Result :

y.ticks() = 0,20,40,60,80,100,120,140,160,180,200
y(0) = 0.00
y(1) = 0.05
y(2) = 0.10
y(3) = 0.15
y(4) = 0.20
y(5) = 0.25
y(6) = 0.30
y(7) = 0.35
y(8) = 0.40
y(9) = 0.45
y(10) = 0.50
y(11) = 0.55
y(12) = 0.60
y(13) = 0.65
y(14) = 0.70
y(15) = 0.75
y(16) = 0.80
y(17) = 0.85



For practical conclusion, domain is the divider whereas range is the data itself.

Hope this helps ....