From 850165503aac1dc7e2c226154a0cca5b7fe4deb1 Mon Sep 17 00:00:00 2001 From: ronakrrb Date: Thu, 9 Apr 2015 15:21:14 +0530 Subject: [PATCH 1/3] Added funnel chart layout built in d3.js --- funnel/README.md | 17 ++++++++ funnel/funnel.js | 111 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 funnel/README.md create mode 100644 funnel/funnel.js diff --git a/funnel/README.md b/funnel/README.md new file mode 100644 index 0000000..4f4561d --- /dev/null +++ b/funnel/README.md @@ -0,0 +1,17 @@ +# Funnel Chart Layout built in d3.js + +Demo: + +```js +var funnel = d3.funnel() + .size([width,height]) + .mouth([width,height]) // width and height of the rectangle in the funnel + .value(function(d) { return d.value; }); +``` + +``` +var g = d3.selectAll(".funnel-group") + .data(funnel(data)) + .enter().append("g") + .attr("class", "funnel-group"); +`` diff --git a/funnel/funnel.js b/funnel/funnel.js new file mode 100644 index 0000000..1c6333f --- /dev/null +++ b/funnel/funnel.js @@ -0,0 +1,111 @@ +d3.funnel = function() { + var size = [1,1], + mouth = [1,1], + sort_data, + value = function(d) { return d.value}, + coordinates; + var percentageValues = function (data) { + var values = data.map(value); + var percentValues = data.map(function (d,i){ + d.value = +values[i]; + var weight_max = d3.max(data, function (d,i) { + return d.value; + }) + return d.value/weight_max*100; + }); + percentValues.sort(function(a,b){ + return b-a; + }); + return percentValues; + } + var coordinatesCalculation = function(data){ + var w = size[0], + h = size[1], + rw = mouth[0], //rect width + rh = mouth[1], //rect height + tw = (w - rw)/2, //triangle width + th = h - rh, //triangle height + height1=0, + height2=0, + height3=0, + merge = 0, + coordinates = [], + ratio = tw/th, + area_of_trapezium = (w + rw) / 2 * th, + area_of_rectangle = rw * rh, + total_area = area_of_trapezium + area_of_rectangle, + percent_of_rectangle = area_of_rectangle / total_area * 100; + for (var i=data.length-1; i>=0; i--){ + var selectedPercentValues = percentageValues(data)[i]; + if (percent_of_rectangle>=selectedPercentValues){ + height3 = selectedPercentValues / percent_of_rectangle * rh; + height1 = h - height3; + if (i===data.length-1){ + coordinates[i] = {"values":[{"x":(w-rw)/2,"y":height1},{"x":(w-rw)/2,"y":h},{"x":((w-rw)/2)+rw,"y":h},{"x":((w-rw)/2)+rw,"y":height1}]}; + }else{ + coordinates[i] = {"values":[{"x":(w-rw)/2,"y":height1},coordinates[i+1].values[0],coordinates[i+1].values[3],{"x":((w-rw)/2)+rw,"y":height1}]}; + } + }else{ + var area_of_element = ((selectedPercentValues)/100 * total_area) - area_of_rectangle, + a = 2 * ratio, + b = 2 * rw, + c = 2 * area_of_element; + height2 = (-b + Math.sqrt(Math.pow(b,2) - (4 * a * -c))) / (2 * a); + height1 = h - height2 - rh; + var base = 2*(ratio * height2)+rw, + xwidth = (w-base)/2; + + if(merge===0){ + if (i===data.length-1){ + coordinates[i] = {"values":[{"x":xwidth,"y":height1},{"x":(w-rw)/2,"y":th},{"x":(w-rw)/2,"y":h},{"x":((w-rw)/2)+rw,"y":h},{"x":((w-rw)/2)+rw,"y":th},{"x":base+xwidth,"y":height1}]}; + }else{ + coordinates[i] = {"values":[{"x":xwidth,"y":height1},{"x":(w-rw)/2,"y":th},coordinates[i+1].values[0],coordinates[i+1].values[3],{"x":((w-rw)/2)+rw,"y":th},{"x":base+xwidth,"y":height1}]}; + } + } + else{ + var coindex; + if(coordinates[i+1].values.length===6){ + coindex = 5; + }else{ + coindex = 3; + } + coordinates[i] = {"values":[{"x":xwidth,"y":height1},coordinates[i+1].values[0],coordinates[i+1].values[coindex],{"x":base+xwidth,"y":height1}]}; + } + merge = 1; + } + } + return coordinates; + } + function funnel(data) { + var i = 0, + coordinates = coordinatesCalculation(data); + data.sort(function(a,b) { + return b.value - a.value; + }) + data.forEach(function(){ + data[i].coordinates = coordinates[i].values; + i++; + }) + return data; + } + funnel.size = function(s){ + if (s.length!==2){ + } else { + size = s; + } + return funnel; + } + funnel.mouth = function(m){ + if (m.length!==2){ + } else { + mouth = m; + } + return funnel; + } + funnel.value = function(v) { + if (!arguments.length) return value; + value = v; + return funnel; + }; + return funnel; +} \ No newline at end of file From a7c0c5c9dcfc6c800efb109ac48b86ab6e15b1ae Mon Sep 17 00:00:00 2001 From: ronakrrb Date: Thu, 9 Apr 2015 15:26:02 +0530 Subject: [PATCH 2/3] Fixed Readme.md --- funnel/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/funnel/README.md b/funnel/README.md index 4f4561d..217024a 100644 --- a/funnel/README.md +++ b/funnel/README.md @@ -14,4 +14,4 @@ var g = d3.selectAll(".funnel-group") .data(funnel(data)) .enter().append("g") .attr("class", "funnel-group"); -`` +``` From 498244f2235b7d1fb43befa1353a4fb237294993 Mon Sep 17 00:00:00 2001 From: ronakrrb Date: Thu, 9 Apr 2015 16:16:59 +0530 Subject: [PATCH 3/3] Comments added for if condition --- funnel/funnel.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/funnel/funnel.js b/funnel/funnel.js index 1c6333f..051fd5c 100644 --- a/funnel/funnel.js +++ b/funnel/funnel.js @@ -4,6 +4,7 @@ d3.funnel = function() { sort_data, value = function(d) { return d.value}, coordinates; + var percentageValues = function (data) { var values = data.map(value); var percentValues = data.map(function (d,i){ @@ -18,6 +19,7 @@ d3.funnel = function() { }); return percentValues; } + var coordinatesCalculation = function(data){ var w = size[0], h = size[1], @@ -35,11 +37,16 @@ d3.funnel = function() { area_of_rectangle = rw * rh, total_area = area_of_trapezium + area_of_rectangle, percent_of_rectangle = area_of_rectangle / total_area * 100; + for (var i=data.length-1; i>=0; i--){ var selectedPercentValues = percentageValues(data)[i]; + + // If the percentage of area of rectangle is greater than the lowest percentage data, then the data fits into the rectangle else that data will be partially in the rectangle and partially in the trapezium. if (percent_of_rectangle>=selectedPercentValues){ height3 = selectedPercentValues / percent_of_rectangle * rh; height1 = h - height3; + + // If its the first data point then use the given weight to calculate the co-ordinate else take the co-ordinates from the previous data. if (i===data.length-1){ coordinates[i] = {"values":[{"x":(w-rw)/2,"y":height1},{"x":(w-rw)/2,"y":h},{"x":((w-rw)/2)+rw,"y":h},{"x":((w-rw)/2)+rw,"y":height1}]}; }else{ @@ -50,11 +57,12 @@ d3.funnel = function() { a = 2 * ratio, b = 2 * rw, c = 2 * area_of_element; - height2 = (-b + Math.sqrt(Math.pow(b,2) - (4 * a * -c))) / (2 * a); + height2 = (-b + Math.sqrt(Math.pow(b,2) - (4 * a * -c))) / (2 * a); //Quadratic equation height1 = h - height2 - rh; var base = 2*(ratio * height2)+rw, xwidth = (w-base)/2; - + + // If the data is sharing the rectangle and trapeziums area (merge=0) or is the data using the area of trapezium only (merge=1). if(merge===0){ if (i===data.length-1){ coordinates[i] = {"values":[{"x":xwidth,"y":height1},{"x":(w-rw)/2,"y":th},{"x":(w-rw)/2,"y":h},{"x":((w-rw)/2)+rw,"y":h},{"x":((w-rw)/2)+rw,"y":th},{"x":base+xwidth,"y":height1}]}; @@ -108,4 +116,4 @@ d3.funnel = function() { return funnel; }; return funnel; -} \ No newline at end of file +}