D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
amerval
Full window
Github gist
Income distribution in New Zealand - 2006
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Treasury</title> <script type="text/javascript" src="https://d3js.org/d3.v3.min.js"></script> <style> body { margin: 0; background-color: DarkGray; font-family: "nyt-cheltenham",georgia,Lucida sans, "times new roman",times,serif; } #container { width: 900px; margin-left: auto; margin-right: auto; margin-top: 50px; padding: 50px; background-color: white; box-shadow: 3px 3px 5px 6px #ccc; } h1 { font-size: 28px; border-top: solid 8px #807166; border-bottom: solid 8px #807166; } p { font-size: 14px; line-height: 18px; padding: 30px; border-bottom: solid 2px #222222; } text.txt { font-family: tahoma; } rect.backlistener { fill: none; pointer-events: all; } rect.background { fill: none; pointer-events: all; } rect.current { pointer-events: all; stroke: #222222; stroke-width: 2; } .axis path, .axis line { fill: none; stroke: black; shape-rendering: crispEdges; } .axis text { font-family: tahoma; font-size: 12px; } #tooltip { text-align: center; position: absolute; width: 200px; height: auto; padding: 10px; background-color: white; -webkit-border-radius: 10px; -moz-border-radius: 10px; border-radius: 10px; -webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); -moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); pointer-events: none; } #tooltip.hidden { display: none; } #tooltip p { margin: 0; font-family: tahoma; font-size: 16px; line-height: 20px; } #basettip { text-align: center; position: absolute; width: 200px; height: auto; padding: 10px; background-color: white; -webkit-border-radius: 10px; -moz-border-radius: 10px; border-radius: 10px; -webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); -moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); pointer-events: none; } #basettip.hidden { display: none; } #basettip p { margin: 0; font-family: tahoma; font-size: 16px; line-height: 20px; } #source { text-align: center; position: absolute; width: 200px; height: auto; padding: 10px; background-color: white; -webkit-border-radius: 10px; -moz-border-radius: 10px; border-radius: 10px; -webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); -moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); pointer-events: none; } #source.hidden { display: none; } #source p { margin: 0; font-family: tahoma; font-size: 16px; line-height: 20px; } </style> </head> <body> <div id="container"> <h1> Income distribution in New Zealand </h1> <p> This graphs shows the distribution of income in New Zealand as recorded by the Census 2006. Click on the buttons to see the breakdown per gender, ethnicity or age group. Click on the graph area to spread the bars and click again to regroup them. (<em>Please let the animation finish before clicking again.</em>) </p> <button id="byGender">View by Gender</button> <button id="byEthni">View by Ethnicity</button> <button id="byAge">View by Age group</button> </div> <div id="tooltip" class="hidden"> <p><span id="people"> . </span></p> <p>That is <span id="percent"> 100.0 </span>% of this income tier.</p> </div> <div id="basettip" class="hidden"> <p><span id="people"> . </span></p> <p>That is <span id="percent"> 100.0 </span>% of the NZ population.</p> </div> <div id="source" class="hidden"> <p>*source: 2006 census</p> </div> <script type="text/javascript"> var h=600, w=900, margin=5, padding=20, wside = 300; var maxw = 400, maxwcur; var maxVal; var data, dnest, nodes; var NbLines; var caseDisplay; var xScale = d3.scale.linear(), spreadScale = d3.scale.ordinal(), yScale = d3.scale.linear().domain([0,50000,130000]).range([0,2*h/3,h]), yScaleAx = d3.scale.linear().domain([0,50000,130000]).range([h,h/3,0]), wScale = d3.scale.linear(), oScale = d3.scale.linear().domain([0,1]).range([0.2,0.9]) areaScale = d3.scale.linear(), cScale = d3.scale.linear().range(["#0033FF","#CC0000"]) toUnit = d3.scale.ordinal().rangeBands([0,1]) //cScale = d3.scale.category10(); var commasFormatter = d3.format(",.0f"); var yAxis = d3.svg.axis() .scale(yScaleAx) // scale to be used .orient("right") // position of the text (values) .tickValues([0,5000,10000,20000,50000,70000,100000]) .tickFormat(function(d) { return "$" + commasFormatter(d); }); var entier = d3.format(",.0f"), perc = d3.format(".1f"); var base, current, curlist; var listGender=["male","female"], listEthni = ["European","Maori","Pacific","Asian","Other"], listAge = ["15-19","20-29","30-39","40-49","50-59","60-69","70-79","80+"], nodesw = []; var hierarchy = d3.layout.partition().sort(null); var svg = d3.select("#container").append("svg") .attr("width", w+wside+padding) .attr("height", h+2*padding) ; var gmain = svg.append("g") //.attr("transform", "translate(" +(padding)+ "," +padding+ ")" ); var gside = svg.append("g") .attr("transform", "translate(" +(100+padding+w)+ "," +padding+ ")" ); gmain.append("rect") // background rectangle to listen to click events .attr("class", "background") .attr("width", w) .attr("height", h) .attr("transform", "translate(" +padding/2+ "," +padding/2+ ")" ) // .attr("stroke","#DDDDDD").attr("stroke-width",2).attr("stroke-opacity",0.5) .attr("fill","green").attr("opacity",0.3) .on("click", back); // "back" function will return to higher level d3.csv("incDistr.csv", function(dd){ data = dd; NbLines = data.length; for (ii=0;ii<NbLines;ii++){ data[ii].value = parseFloat(data[ii].value); data[ii].tierh = parseFloat(data[ii].tierh); data[ii].tierPos = parseFloat(data[ii].tierPos); } dnest = d3.nest() .key(function(d) { return d.tier; }) .entries(dd); nodes = {name: "totpop", children:[],tierh:0, tierPos:0}; for (ii=0;ii<dnest.length;ii++){ var tmp = [], add; tmp = {name: dnest[ii].key, children:[], tierh:dnest[ii].values[0].tierh, tierPos:dnest[ii].values[0].tierPos} for (jj=0;jj<dnest[ii].values.length;jj++){ tmp.children.push(dnest[ii].values[jj]) } nodes.children.push(tmp) } nodes = hierarchy(nodes); NbLines = nodes.length; maxVal = 0; var htmp; for (ii=1;ii<NbLines;ii++){if(nodes[ii].value>maxVal){maxVal=nodes[ii].value;}} areaScale.domain([0,maxVal]).range([0,(maxw*yScale(5000))]); for (ii=1;ii<NbLines;ii++){ if (nodes[ii].tierPos>50000) { nodes[ii].h = (yScale(50000+nodes[ii].tierh)-yScale(50000)-margin) nodes[ii].w = (areaScale(nodes[ii].value)/(yScale(50000+nodes[ii].tierh)-yScale(50000))); } else { nodes[ii].w = (areaScale(nodes[ii].value)/yScale(nodes[ii].tierh)) nodes[ii].h = (yScale(nodes[ii].tierh)-margin)} } nodesw[0] = 0; // display scale var aa = Math.sqrt(areaScale(10000)); scale = gside.append("rect") .attr("rx",6).attr("ry",6) .attr("y",100).attr("x",5) .attr("height",aa) .attr("width",aa) .attr("fill","none") .attr("stroke","#BBBBBB").style("stroke-dasharray",("3,2")).attr("stroke-width",2); gside.append("text").attr("class","txt") .attr("y", 100+aa/2).attr("x",aa+15) .attr("font-size","14px").attr("fill","#BBBBBB") .attr("text-anchor","left") .text("10,000 people"); // first display: level 0 caseDisplay = 0; base = gmain.selectAll(".base") .data(nodes).enter() .append("rect").attr("class","base") .attr("rx",6).attr("ry",6) .attr("y",function(d){return (h-yScale(d.tierPos));}) .attr("height",function(d,i){return (d.depth==1)?d.h:0}) .attr("width",function(d,i){return (d.depth==1)?d.w:0}) .attr("x",function(d,i){return (d.depth==1)?(w/2-d.w/2):0}) .attr("fill","#BBBBBB").attr("opacity",0.7); base.on("mouseover", basemov) .on("mouseout", basemout) svg.append("g") .attr("class","axis") .attr("transform", "translate(0,"+(padding/2)+")") .call(yAxis) ; d3.select("#byGender").on("click", function () { caseDisplay = 1; maxwcur = 0; // Re-organise nodes dnest = d3.nest() .key(function(d) { return d.tier; }) .key(function(d) { return d.gender; }) .entries(data); nodes = {name: "totpop", children:[],tierh:0, tierPos:0}; for (ii=0;ii<dnest.length;ii++){ var tmp = [], add; tmp = {name: dnest[ii].key, children:[], tierh:dnest[ii].values[0].values[0].tierh, tierPos:dnest[ii].values[0].values[0].tierPos} for (jj=0;jj<dnest[ii].values.length;jj++){ add = {name: dnest[ii].values[jj].key, children:[], tierh:dnest[ii].values[jj].values[0].tierh, tierPos:dnest[ii].values[jj].values[0].tierPos} for (kk=0;kk<dnest[ii].values[jj].values.length;kk++){ add.children.push(dnest[ii].values[jj].values[kk]) } tmp.children.push(add) } nodes.children.push(tmp) } nodes = hierarchy(nodes); toUnit.domain(listGender) cScale.domain([0,(1-toUnit.rangeBand())]) spreadScale.domain(listGender) curlist = listGender; createLayer(); }) d3.select("#byAge").on("click", function () { caseDisplay = 2; maxwcur = 0; // Re-organise nodes dnest = d3.nest() .key(function(d) { return d.tier; }) .key(function(d) { return d.agetier; }) .entries(data); nodes = {name: "totpop", children:[],tierh:0, tierPos:0}; for (ii=0;ii<dnest.length;ii++){ var tmp = [], add; tmp = {name: dnest[ii].key, children:[], tierh:dnest[ii].values[0].values[0].tierh, tierPos:dnest[ii].values[0].values[0].tierPos} for (jj=0;jj<dnest[ii].values.length;jj++){ add = {name: dnest[ii].values[jj].key, children:[], tierh:dnest[ii].values[jj].values[0].tierh, tierPos:dnest[ii].values[jj].values[0].tierPos} for (kk=0;kk<dnest[ii].values[jj].values.length;kk++){ add.children.push(dnest[ii].values[jj].values[kk]) } tmp.children.push(add) } nodes.children.push(tmp) } nodes = hierarchy(nodes); toUnit.domain(listAge) cScale.domain([0,(1-toUnit.rangeBand())]) spreadScale.domain(listAge) curlist = listAge; createLayer(); }) d3.select("#byEthni").on("click", function () { caseDisplay = 3; maxwcur = 0; // Re-organise nodes dnest = d3.nest() .key(function(d) { return d.tier; }) .key(function(d) { return d.ethnicity; }) .entries(data); nodes = {name: "totpop", children:[],tierh:0, tierPos:0}; for (ii=0;ii<dnest.length;ii++){ var tmp = [], add; tmp = {name: dnest[ii].key, children:[], tierh:dnest[ii].values[0].values[0].tierh, tierPos:dnest[ii].values[0].values[0].tierPos} for (jj=0;jj<dnest[ii].values.length;jj++){ add = {name: dnest[ii].values[jj].key, children:[], tierh:dnest[ii].values[jj].values[0].tierh, tierPos:dnest[ii].values[jj].values[0].tierPos} for (kk=0;kk<dnest[ii].values[jj].values.length;kk++){ add.children.push(dnest[ii].values[jj].values[kk]) } tmp.children.push(add) } nodes.children.push(tmp) } nodes = hierarchy(nodes); toUnit.domain(listEthni) cScale.domain([0,(1-toUnit.rangeBand())]) spreadScale.domain(listEthni); curlist = listEthni; createLayer(); }) }) function back(){ caseDisplay = 0; gmain.selectAll(".current").transition().duration(500) .attr("x",function(d){ return (d.depth==2)?(w/2+d.xx):0;}) base.on("mouseover", basemov) .on("mouseout", basemout) // gmain.selectAll(".base").transition().duration(1000).delay(500).attr("opacity",1); gmain.selectAll(".base").transition().duration(1000).delay(750).attr("opacity",1); gmain.selectAll(".current").transition().duration(1000).delay(750).attr("opacity",1e-6); gmain.selectAll(".current").transition().delay(1750).remove(); svg.selectAll(".backlistener").remove(); gmain.selectAll(".legend").remove(); } function createLayer() { maxwcur = 0; NbLines = nodes.length; for (ii=1;ii<NbLines;ii++){ if (nodes[ii].tierPos>50000) { nodes[ii].h = (yScale(50000+nodes[ii].tierh)-yScale(50000)-margin) nodes[ii].w = (areaScale(nodes[ii].value)/(yScale(50000+nodes[ii].tierh)-yScale(50000))); } else { nodes[ii].w = (areaScale(nodes[ii].value)/yScale(nodes[ii].tierh)) nodes[ii].h = (yScale(nodes[ii].tierh)-margin)} if (nodes[ii].depth==2) { wScale.domain([0,1]).range([0,nodes[ii].parent.w]); nodes[ii].w = wScale(nodes[ii].dx/nodes[ii].parent.dx); nodes[ii].xx = wScale((nodes[ii].x-nodes[ii].parent.x)/nodes[ii].parent.dx)-nodes[ii].parent.w/2; if (nodes[ii].w > maxwcur){maxwcur = nodes[ii].w;} } } nodesw[0] = 0; current = gmain.selectAll(".current") .data(nodes).enter() .append("rect").attr("class","current") .attr("rx",6).attr("ry",6) .attr("y",function(d){return (h-yScale(d.tierPos));}) .attr("height",function(d,i){return (d.depth==0)?0:d.h}) .attr("width",function(d){return (d.depth==2)?d.w:0}) .attr("x",function(d,i){ var wpar, xtmp; if (d.depth==2){ //console.log(i+", "+d.xx) xtmp = d.xx; if (d.parent.maxdx>0){if (xtmp>d.parent.maxdx){d.parent.maxdx=xtmp; d.parent.wmaxdx=d.w}} else {d.parent.maxdx=xtmp; d.parent.wmaxdx=d.w} } else{xtmp=0} return w/2+xtmp;}) .attr("fill","#BBBBBB").attr("opacity",1e-6) .attr("stroke-opacity",1e-6) .on("click",Spread); spreadScale.rangeBands([maxwcur/2,w+maxwcur/2]); gmain.selectAll(".current").transition().duration(500) .attr("opacity",function(d){ return (d.depth>0)?(oScale(d.dx/d.parent.dx)):(1e-6)}) .attr("fill",function(d) {return cScale(toUnit(d.name))}); gmain.selectAll(".base").on("mouseover",null).on("mouseout",null).on("click",back) gmain.selectAll(".base").transition().duration(500).attr("opacity",1e-6); } function Spread() { gmain.selectAll(".current").transition().duration(500) .attr("x",function(d){var wtmp, xtmp; if (d.depth==2){ wtmp = d3.select(this).attr("width") xtmp = spreadScale(d.name)-wtmp/2; } else{xtmp=0} return xtmp;}) current.on("mouseover", function(d){ var txtmp, txtinc; var xPos = parseFloat(d3.select(this).attr("x")); var yPos = parseFloat(d3.select(this).attr("y")); d3.select(this).attr("stroke-opacity",1); if (d.depth==2){ var aa = d3.select("#tooltip") // .style("left", (xPos+200) + "px") .style("left", (w+300) + "px") // .style("top", (yPos+50) + "px"); .style("top", (h/2) + "px"); var nb = entier(d.value); var percent = perc((d.value/d.parent.value)*100); txtinc = extrTxtInc(d); switch (caseDisplay) { case 1: var bb; (d.name == "male")?(bb="* men "):(bb="* women "); txtmp = nb + bb + txtinc; break; case 3: txtmp = nb + "* people of "+d.name+" ethnicity" + txtinc; break; case 2: txtmp = nb + "* people aged " +d.name+ txtinc; break; } aa.select("#people").text(txtmp) aa.select("#percent").text(percent) d3.select("#tooltip").classed("hidden",false); d3.select("#source") .style("left", (w+300) + "px") .style("top", (h-50) + "px") .classed("hidden",false); } }) .on("mouseout", function(d){ d3.select(this).attr("stroke-opacity",1e-6); d3.select("#tooltip").classed("hidden",true); d3.select("#source").classed("hidden",true); }) // svg.append("svg:rect") // background rectangle to listen to click events // .attr("class", "backlistener") // .attr("width", w) // .attr("height", h) // .attr("transform", "translate(" +padding/2+ "," +padding/2+ ")" ) // .on("click", back); gmain.selectAll(".legend") .data(curlist).enter() .append("g").attr("class","legend") .attr("transform", function(d) { return "translate(" +spreadScale(d)+ "," +(h-20)+ ")" }) .append("text").attr("class","txt") .attr("y", 0) .attr("x",0) .attr("font-size","14px") .attr("text-anchor","middle") .attr("opacity",1e-6) .text(function (d){return d}); gmain.selectAll(".txt").transition().duration(500).delay(500) .attr("opacity",1) } function extrTxtInc(d) { var txtmp, t0 =" earn ", t1, t2=" yearly"; switch (d.parent.name) { case "No Income": t0 = " have " t1 = "no income"; t2 = ""; break; case "<5000": t1 = "between $1 and $5,000"; break; case "<10000": t1 = "between $5,000 and $10,000"; break; case "<15000": t1 = "between $10,000 and $15,000"; break;; case "<20000": t1 = "between $15,000 and $20,000"; break; case "<25000": t1 = "between $20,000 and $25,000"; break; case "<30000": t1 = "between $25,000 and $30,000"; break; case "<40000": t1 = "between $30,000 and $40,000"; break; case "<50000": t1 = "between $40,000 and $50,000"; break; case "<70000": t1 = "between $50,000 and $70,000"; break; case "<100000": t1 = "between $70,000 and $100,000"; break; case ">100000": t1 = "over $100,000"; break; } txtmp = t0+t1+t2; return txtmp; } function basemov(d) { var txtmp, txtinc; d3.select(this).attr("opacity",0.95); if (d.depth==1){ var aa = d3.select("#basettip") .style("left", (w+300) + "px") .style("top", (h/2) + "px"); var nb = entier(d.value); var percent = perc((d.value/d.parent.value)*100); var t0 =" earn ", t1, t2=" yearly"; switch (d.name) { case "No Income": t0 = " have " t1 = "no income"; t2 = ""; break; case "<5000": t1 = "between $1 and $5,000"; break; case "<10000": t1 = "between $5,000 and $10,000"; break; case "<15000": t1 = "between $10,000 and $15,000"; break;; case "<20000": t1 = "between $15,000 and $20,000"; break; case "<25000": t1 = "between $20,000 and $25,000"; break; case "<30000": t1 = "between $25,000 and $30,000"; break; case "<40000": t1 = "between $30,000 and $40,000"; break; case "<50000": t1 = "between $40,000 and $50,000"; break; case "<70000": t1 = "between $50,000 and $70,000"; break; case "<100000": t1 = "between $70,000 and $100,000"; break; case ">100000": t1 = "over $100,000"; break; } txtmp = nb +"* NZ residents"+t0+t1+" annually"; aa.select("#people").text(txtmp) aa.select("#percent").text(percent) d3.select("#basettip").classed("hidden",false); d3.select("#source") .style("left", (w+300) + "px") .style("top", (h) + "px") .classed("hidden",false); } } function basemout(d){ d3.select(this).attr("opacity",0.7); d3.select("#basettip").classed("hidden",true); d3.select("#source").classed("hidden",true); } </script> </body> </html>
Modified
http://d3js.org/d3.v3.min.js
to a secure url
https://d3js.org/d3.v3.min.js