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 ....

Saturday, May 21, 2011

Bursting Circular Lines


<html>
    <head>
    <script type="text/javascript"  src="protovis-r3.2.js"></script>
    </head>
    <body>
    <script type="text/javascript+protovis"
    var inner_radius = 120,
        centerx = 200,
        centery = 200;
    var data = pv.range(0,12,1);
    var vis = new pv.Panel()
                .fillStyle("yellow")
                .width(400)
                .height(400);
        
    vis.add(pv.Wedge)
        .left(centerx)
        .bottom(centery)
        .outerRadius(150)
        .innerRadius(inner_radius)
        .fillStyle("none")
        .strokeStyle("black")
        .lineWidth(0.5)
        .angle(Math.PI * 2);
        
    vis.add(pv.Dot)
        .data(data)
        .shape("tick")
        .left(function(d) centerx)
        .angle(function(d) (2 * Math.PI/12) * (this.index+1))
        .bottom(centery)
        .fillStyle("none")
        .strokeStyle("black")
        .lineWidth(1)
        .size(50);    
    vis.render();
    </script>
    </body>
</html>

Friday, May 20, 2011

Wedge


<html>
    <head>
    <script type="text/javascript"  src="protovis-r3.2.js"></script>
    </head>
    <body>
    <script type="text/javascript+protovis"
    var w = 150,
        h = 150,
        l = 75,
        b = 75;
    var data = [222], sum = pv.sum(data);
    var colors = ["red""yellow""green"];
    
    function calculateangle(data, idx)
    {
        var sum = pv.sum(data);
        
        if(idx > 0)
        {
            var hasil = 0;
            for(i=0; i<idx; i++) hasil+=data[i];
            return Math.PI + (hasil / sum * Math.PI);
        }
        else
        {
            return Math.PI;
        }
    }
    
    new pv.Panel()
        .width(w)
        .height(h)
        .add(pv.Wedge)
        .data(data)
        .left(l)
        .bottom(b)
        .innerRadius(30)
        .outerRadius(70)
        .startAngle(function(d) calculateangle(data, this.index))
        .angle(function(d) (d / sum * Math.PI))
        .fillStyle(function() colors[this.index])
        .root.render();
    </script>
    </body>
</html>

PV Function Samples (Part 2)

pv.normalize

Description from Protovis docs : Returns a normalized copy of the specified array, such that the sum of the returned elements sum to one. If the specified array is not an array of numbers, an optional accessor function f can be specified to map the elements to numbers. For example, if array is an array of objects, and each object has a numeric property "foo", the expression



  • pv.normalize(array, function(d) d.foo) 

returns a normalized array on the "foo" property. If an accessor function is not specified, the identity function is used. Accessor functions can refer to this.index.


Sample Script

<html>
    <head>
    <script type="text/javascript"  src="protovis-r3.2.js"></script>
    </head>
    <body>
    <script type="text/javascript+protovis">  
    document.getElementById("pvfunction").innerHTML += pv.normalize([123]);
    </script>
    <div id="pvfunction">
    </div>    
    </body>
</html>

Script Result

0.16666666666666666,0.3333333333333333,0.5


pv.cross
Description from Protovis docs : Given two arrays a and b, returns an array of all possible pairs of elements [ai, bj]. The outer loop is on array a, while the inner loop is on b, such that the order of returned elements is [a0, b0], [a0, b1], ... [a0, bm], [a1, b0], [a1, b1], ... [a1, bm], ... [an, bm]. If either array is empty, an empty array is returned.


Sample Script

<html>
    <head>
    <script type="text/javascript"  src="protovis-r3.2.js"></script>
    </head>
    <body>
    <script type="text/javascript+protovis"
    var array1 = pv.range(0,12,1);
    var array2 = [200];
    var array3 = pv.cross(array1, array2);
    for(i=0; i<array3.length;i++)
    {
        document.getElementById("pvfunction").innerHTML 
            += "index " + i + " = [" + array3[i] + "]<BR />";
    }
    </script>
    <div id="pvfunction">
    </div>    
    </body>
</html>


Script Result

index 0 = [0,200]
index 1 = [1,200]
index 2 = [2,200]
index 3 = [3,200]
index 4 = [4,200]
index 5 = [5,200]
index 6 = [6,200]
index 7 = [7,200]
index 8 = [8,200]
index 9 = [9,200]
index 10 = [10,200]
index 11 = [11,200]

pv.Scale.linear Samples

Sample 1

<html>
    <head>
    <script type="text/javascript"  src="protovis-r3.2.js">
    </script>
    </head>
    <body>
    <script type="text/javascript+protovis"
    var w = 1;
    var xlimit = 10;
    var x = pv.Scale.linear(0, xlimit).range(0,w);
    
    document.getElementById("pvfunction").innerHTML 
        =  " var x = pv.Scale.linear(0, xlimit).range(0,w);";    
    document.getElementById("pvfunction").innerHTML 
        += " <br /> | x.domain() = " + x.domain();
    document.getElementById("pvfunction").innerHTML 
        += " <br /> | x.range() = " + x.range();
    document.getElementById("pvfunction").innerHTML 
        += " <br /> | x.ticks() = " + x.ticks();
    for(i = 0;  i <= xlimit; i++)
    {
        document.getElementById("pvfunction").innerHTML 
            += " <br /> | x(" + i + ") = " + x(i);
    }
    </script>
    <div id="pvfunction">
    </div>    
    </body>
</html>

Result 1

Sample 2

<html>
    <head>
    <script type="text/javascript"  src="protovis-r3.2.js"></script>
    </head>
    <body>
    <script type="text/javascript+protovis"
    var w = 100;
    var xlimit = 5;
    var x = pv.Scale.linear(0, xlimit).range(0,w);
    
    document.getElementById("pvfunction").innerHTML 
        =  " var x = pv.Scale.linear(0, xlimit).range(0,w);";    
    document.getElementById("pvfunction").innerHTML 
        += " <br /> | x.domain() = " + x.domain();
    document.getElementById("pvfunction").innerHTML 
        += " <br /> | x.range() = " + x.range();
    document.getElementById("pvfunction").innerHTML 
        += " <br /> | x.ticks() = " + x.ticks();
    for(i = 0;  i <= xlimit; i++)
    {
        document.getElementById("pvfunction").innerHTML 
            += " <br /> | x(" + i + ") = " + x(i);
    }
    </script>
    <div id="pvfunction">
    </div>    
    </body>
</html>


Result 2

Thursday, May 19, 2011

PV Range and Array Map

<script type="text/javascript+protovis"
function go(x)
{
    return 2 * x;
}

var range1 = pv.range(10).map(go);
alert(range1); //0,2,4,6,8,10,12,14,16,18
</script>

PV Function Examples (Part 1)

pv.search
var mydata = ["a""b""c"];
pv.search(mydata, "a"); // result = 0
pv.search(mydata, "c"); // result = 2


pv.transpose
var mydata = [["a""b""c"]];//result = [["a", "b", "c"]] 
alert(mydata[0]);   
pv.transpose(mydata);//result = [["a"], ["b"], ["c"]] 
alert(mydata[0]); 


pv.uniq
var mydata = ["a""c""b""c"]; 
pv.uniq(mydata); //result = ["a", "c" , "b"] 
alert(pv.uniq(mydata));

pv.range
alert(pv.range(0,10,1)); //[1,2,3,4,5,6,7,8,9,10]
alert(pv.range(0,10,1)); //[0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
alert(pv.range(10)); //[1,2,3,4,5,6,7,8,9,10]


Monday, May 16, 2011

Another Bar Chart (Part 1)

<script type="text/javascript+protovis">
var data1 = [11.21.71.5.7];
var data2 = [1.21.11.51.7.9];
var width = 25;
var barspace = 35;

var labeldata = ["a""b""c""d""e"];

var panel = new pv.Panel()
        .width(500)
        .height(150);
        
var bar = panel.add(pv.Bar)
        .data(data1)
        .bottom(0)
        .width(width)
        .height(function(d) d * 80)
        .left(function() this.index * (width + barspace));
var bar2 = panel.add(pv.Bar)
        .data(data2)
        .bottom(0)
        .width(width)
        .fillStyle("red")
        .height(function(d) d * 80)
        .left(function() this.index * (width + barspace) + width);
        
        
bar.add(pv.Label)
    .data(labeldata)
    .top(function() bar.top())            
    .left(function() bar.left()+3);
bar.add(pv.Label)
    .textStyle("white");
    
bar2.add(pv.Label)
    .data(labeldata)
    .top(function() bar2.top())            
    .left(function() bar2.left()+3);
    
bar2.add(pv.Label)
    .textStyle("white");
            
panel.render();
</script>


Simple Sorted Bar Chart

Alternative 1

<script type="text/javascript+protovis">
var mydata = [11.21.71.5.7];
mydata.sort();
mydata.reverse();
new pv.Panel()
        .width(150)
        .height(150)
        .add(pv.Bar)
        .data(mydata)
        .bottom(0)
        .width(20)
        .height(function(d) d * 80)
        .left(function() this.index * 25)
        .anchor("top")
        .add(pv.Label)
        .root.render();
</script>




Alternative 2

<script type="text/javascript+protovis">
var mydata = [11.21.71.5.7];
mydata.sort();
mydata.reverse();
var panel = new pv.Panel()
        .width(150)
        .height(150);
        
var bar = panel.add(pv.Bar)
        .data(mydata)
        .bottom(0)
        .width(20)
        .height(function(d) d * 80)
        .left(function() this.index * 25);
        
var label = bar.add(pv.Label)
            .top(function() bar.top())
            .root.render();
</script>




Alternative 3

<script type="text/javascript+protovis">
var mydata = [11.21.71.5.7];
var labeldata = ["a""b""c""d""e"];

mydata.sort();
mydata.reverse();
var panel = new pv.Panel()
        .width(150)
        .height(150);
        
var bar = panel.add(pv.Bar)
        .data(mydata)
        .bottom(0)
        .width(20)
        .height(function(d) d * 80)
        .left(function() this.index * 25);
        
var label = bar.add(pv.Label)
            .data(labeldata)
            .top(function() bar.top())            
            .left(function() bar.left()+3)

var label = bar.add(pv.Label)
            .textStyle("white");
            
panel.render();
</script>



Related Post :


Saturday, May 14, 2011

Line Chart - Multiple Absis

<script type="text/javascript+protovis">  
/* Sizing and scales. */ 
var w = 400
    h = 400
    maxx = 110
    maxy = 1
    maxy2 = 100
    x = pv.Scale.linear(0, maxx).range(0, w), 
    y = pv.Scale.linear(0, maxy).range(0, h), 
    y2 = pv.Scale.linear(0, maxy2).range(0, h), 
    c = pv.Scale.log(1100).range("orange""brown"); 

/* The root panel. */ 
var vis = new pv.Panel() 
    .width(w+40
    .height(h+20
    .bottom(20
    .left(20
    .right(10
    .top(5); 

/* Y-axis and ticks. */ 
vis.add(pv.Rule) 
    .data(y.ticks()) 
    .width(w)
    .bottom(y) 
    .strokeStyle(function(d) d ? "#eee" : "#000"
    .anchor("left").add(pv.Label) 
    //.visible(function(d) d > 0 && d < 1) 
    .text(y.tickFormat); 

// For y2
vis.add(pv.Rule)
    .data(y2.ticks()) 
    .width(w)
    .bottom(y2) 
    .strokeStyle(function(d) d ? "#eee" : "#000"
    .anchor("right").add(pv.Label).left(w+5)
    //.visible(function(d) d > 0 && d < 1) 
    .text(y.tickFormat)
    .textStyle("#aaa"); 

/* X-axis and ticks. */ 
vis.add(pv.Rule) 
    .data(x.ticks()) 
    .bottom(0)
    .height(h)
    .left(x) 
    .strokeStyle(function(d) (d==0 || d==110) ? "#000" : "#eee"
    .anchor("bottom").add(pv.Label) 
    .visible(function(d) d > 0 && d <= 100
    .text(x.tickFormat); 

var mydata1 = [[10,0.5],[20,0.3],[30,0.2],[40,0.4],[50,0.6],[60,0.6]];     
var mydata2 = [[10,0.1],[20,0.2],[30,0.4]];     
var mydata3 = [[10,43],[20,62],[30,40],[40,45],[50,43],[60,42],[70,40],[80,45]]; 

var line1 = vis.add(pv.Line) 
    .data(mydata1) 
    .left(function(d) d[0] * w/maxx) 
    .bottom(function(d) d[1] * h / maxy); 
    
var line2 = vis.add(pv.Line) 
    .data(mydata2) 
    .left(function(d) d[0] * w/maxx) 
    .bottom(function(d) d[1] * h / maxy) 
    .strokeStyle("red");

var line3 = vis.add(pv.Line) 
    .data(mydata3) 
    .left(function(d) d[0] * w/maxx) 
    .bottom(function(d) d[1] * h / maxy2) 
    .strokeStyle("green");
    
vis.add(pv.Dot)
    .left(mydata1[mydata1.length-1][0] * w/maxx)
    .bottom(mydata1[mydata1.length-1][1]  * h / maxy)
    .anchor("right")
    .add(pv.Label)
    .text("Legend 1")
    .textStyle("#1F77B4");
    
vis.add(pv.Dot)
    .left(mydata2[mydata2.length-1][0] * w/maxx)
    .bottom(mydata2[mydata2.length-1][1]  * h / maxy)
    .strokeStyle("red")
    .radius(0)
    .anchor("right")
    .add(pv.Label)
    .text("Legend 2")
    .textStyle("red");

vis.add(pv.Dot)
    .left(mydata3[mydata3.length-1][0] * w/maxx)
    .bottom(mydata3[mydata3.length-1][1]  * h/maxy2)
    .strokeStyle("green")
    .radius(0)
    .anchor("right")
    .add(pv.Label)
    .text("Legend 3")
    .textStyle("green");
    
vis.render(); 

</script>

Multi Absis

Friday, May 13, 2011

Line Chart with Legends

<script type="text/javascript+protovis">  
/* Sizing and scales. */ 
var w = 400
    h = 400
    maxx = 99
    maxy = 1
    x = pv.Scale.linear(0, maxx).range(0, w), 
    y = pv.Scale.linear(0, maxy).range(0, h), 
    c = pv.Scale.log(1100).range("orange""brown"); 

/* The root panel. */ 
var vis = new pv.Panel() 
    .width(w) 
    .height(h) 
    .bottom(20
    .left(20
    .right(10
    .top(5); 

/* Y-axis and ticks. */ 
vis.add(pv.Rule) 
    .data(y.ticks()) 
    .bottom(y) 
    .strokeStyle(function(d) d ? "#eee" : "#000"
    .anchor("left").add(pv.Label) 
    .visible(function(d) d > 0 && d < 1
    .text(y.tickFormat); 

/* X-axis and ticks. */ 
vis.add(pv.Rule) 
    .data(x.ticks()) 
    .left(x) 
    .strokeStyle(function(d) d ? "#eee" : "#000"
    .anchor("bottom").add(pv.Label) 
    .visible(function(d) d > 0 && d < 100
    .text(x.tickFormat); 

var mydata1 = [[10,0.5],[20,0.3],[30,0.2],[40,0.4],[50,0.6],[60,0.6]];     
var mydata2 = [[10,0.1],[20,0.2],[30,0.4]];     

var line1 = vis.add(pv.Line) 
    .data(mydata1) 
    .left(function(d) d[0] * w/maxx) 
    .bottom(function(d) d[1] * h / maxy); 
    
var line2 = vis.add(pv.Line) 
    .data(mydata2) 
    .left(function(d) d[0] * w/maxx) 
    .bottom(function(d) d[1] * h / maxy) 
    .strokeStyle("red");
    
vis.add(pv.Dot)
    .left(mydata1[mydata1.length-1][0] * w/maxx)
    .bottom(mydata1[mydata1.length-1][1]  * h / maxy)
    .anchor("right")
    .add(pv.Label)
    .text("Legend 1")
    .textStyle("#1F77B4");
    
vis.add(pv.Dot)
    .left(mydata2[mydata2.length-1][0] * w/maxx)
    .bottom(mydata2[mydata2.length-1][1]  * h / maxy)
    .strokeStyle("red")
    .radius(0)
    .anchor("right")
    .add(pv.Label)
    .text("Legend 2")
    .textStyle("red");
    
vis.render(); 

</script>

Two Line Charts