Layout of US House & Senate members in an arc using D3. Based on code from http://en.wikipedia.org/wiki/User_talk:Slashme/parliament.py
A Pen by Liji Jinaraj on CodePen.
| <div class="chart"> | |
| </div> |
| /** | |
| * ref http://en.wikipedia.org/wiki/User_talk:Slashme/parliament.py | |
| */ | |
| function parliamentCharts(el, parliament) { | |
| var margin = {top: 10, right: 0, bottom: 0, left: 10}, | |
| width = 500 - margin.left - margin.right, | |
| height = 500 - margin.top - margin.bottom; | |
| var totalSpots = [3, 15, 33, 61, 95, 138, 189, 247, 313, 388, 469, 559, 657, 762, 876, 997, 1126, 1263, 1408, 1560, 1722, 1889, 2066, 2250, 2442, 2641, 2850, 3064, 3289, 3519, 3759, 4005, 4261, 4522, 4794, 5071, 5358, 5652, 5953, 6263, 6581, 6906, 7239, 7581, 7929, 8287, 8650, 9024, 9404]; | |
| var formatNumber = d3.format("n"); | |
| var partyColors = { | |
| "d":{name:"Democrats", color:"#0A5F90"} | |
| , "r":{name:"Republicans", color:"#CC5B2E"} | |
| , "i":{name:"Independents", color:"#333"} | |
| , "vacant":{name:"Vacant", color:"#fff"} | |
| }; | |
| var svg = el.append("svg") | |
| .attr("class", "parliament usa") | |
| .attr("width", width + margin.left + margin.right) | |
| .attr("height", height + margin.top + margin.bottom) | |
| .append("g") | |
| .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | |
| draw(svg, parliament[0].houses[0], 0, 10, {x:5, y:5}); | |
| draw(svg, parliament[0].houses[1], 180, 5, {x:5, y:15}); | |
| drawLegend(svg); | |
| function draw(svgEl, pData, pOffsetAngle, pRows, centerOffset) { | |
| var dcols = []; | |
| var baseRow = 0; | |
| var n = pData.totalMembers; | |
| var maxRadius = 0.2/8; | |
| var partyIndex = 0; | |
| var partyStartIndex = 0; | |
| var dataRef = []; | |
| var direction = pOffsetAngle === 180 ? 1 : 0; | |
| function initDataRef() { | |
| pOffsetAngle *= Math.PI/180; | |
| function calcCol(drow, dcol, dn, dr) { | |
| var da = dcol*(Math.PI-2.0*Math.sin(maxRadius/dr))/(dn-1.0)+Math.sin(maxRadius/dr) + pOffsetAngle; | |
| var dx = Math.cos(da) * dr + 1.75; | |
| var dy = Math.sin(da) * dr; | |
| dataRef.push({ | |
| id: dataRef.length, | |
| row: drow, | |
| col: dcol, | |
| a: da, | |
| x: dx, | |
| y: dy | |
| }); | |
| } | |
| d3.range(1, pRows).forEach(function(drow) { | |
| var dr = (3.0*pRows+4.0*drow-2.0)/(4.0*pRows); | |
| var dn = Math.floor(n/totalSpots[pRows-1]*Math.PI/(2*Math.asin(2.0/(3.0*pRows+4.0*drow-2.0)))); | |
| d3.range(dn).forEach(function(dcol) { | |
| calcCol(drow, dcol, dn, dr); | |
| }); | |
| }); | |
| var dn = n - dataRef.length; | |
| var dr =(7.0*pRows-2.0)/(4.0*pRows); | |
| d3.range(dn).forEach(function(dcol) { | |
| calcCol(0, dcol, dn, dr); | |
| }) | |
| } | |
| initDataRef(); | |
| dataRef = dataRef.sort(function(a,b) { | |
| return a.a - b.a; | |
| }).reverse(); | |
| var gHouse = svgEl.select(".house " + pData.id); | |
| if(gHouse.empty()) { | |
| gHouse = svgEl.append("g") | |
| .attr("class", "house " + pData.id); | |
| } | |
| var gTitle = gHouse.append("g") | |
| .attr("class", "house-heading"); | |
| var center = { | |
| x: Math.cos(90 * Math.PI/180 + pOffsetAngle) * 0.2 + 1.75, | |
| y: Math.sin(90 * Math.PI/180 + pOffsetAngle) * 0.2 | |
| }; | |
| gTitle.append("text") | |
| .attr("class", "house-members") | |
| .attr("text-anchor", "middle") | |
| .attr("x", center.x * 100 + centerOffset.x) | |
| .attr("y", (1.75 - center.y) * 100 + centerOffset.y + (direction * 28)) | |
| .style("fill", "#333") | |
| .text(pData.totalMembers); | |
| center = { | |
| x: Math.cos(90 * Math.PI/180 + pOffsetAngle) * 0.05 + 1.75, | |
| y: Math.sin(90 * Math.PI/180 + pOffsetAngle) * 0.05 | |
| }; | |
| gTitle.append("text") | |
| .attr("class", "house-name") | |
| .attr("text-anchor", "middle") | |
| .attr("x", center.x * 100 + centerOffset.x) | |
| .attr("y", (1.75 - center.y) * 100 + centerOffset.y + (direction * 12)) | |
| .style("fill", "#333") | |
| .text(pData.name); | |
| var spot = -1; | |
| pData.parties.forEach(function(party) { | |
| var gParty = gHouse.select(".party .party-" + party.id); | |
| if(gParty.empty()) { | |
| gParty = gHouse.append("g") | |
| .datum(party) | |
| .attr("class", "party party-" + party.id); | |
| } | |
| var circles = gParty.selectAll(".node." + pData.id) | |
| .data(d3.range(spot+1, spot+party.totalMembers+1)); | |
| circles.enter().append("circle") | |
| .attr("class", function(d) { | |
| var dref = dataRef[d]; | |
| return "node " + dref.id + " node" + dref.row; | |
| }) | |
| .attr("r", maxRadius * 100); | |
| circles | |
| .attr("cx", function(d) { | |
| var dref = dataRef[d]; | |
| return dref.x * 100 + centerOffset.x; | |
| }) | |
| .attr("cy", function(d) { | |
| var dref = dataRef[d]; | |
| return (1.75 - dref.y) * 100 + centerOffset.y; | |
| }) | |
| .style("fill", partyColors[party.id].color || "#333"); | |
| circles.exit().remove(); | |
| spot = spot + party.totalMembers; | |
| }); | |
| } | |
| function drawLegend(svg) { | |
| var legend = svg.selectAll(".legend") | |
| .data(d3.map(partyColors).values()) | |
| .enter().append("g") | |
| .attr("class", "legend") | |
| .attr("transform", function(d, i) { return "translate(0," + (i * 20 + 50) + ")"; }); | |
| legend.append("rect") | |
| .attr("x", width - 18) | |
| .attr("width", 18) | |
| .attr("height", 18) | |
| .style("fill", function(d) { return d.color; }); | |
| legend.append("text") | |
| .attr("x", width - 24) | |
| .attr("y", 9) | |
| .attr("dy", ".35em") | |
| .style("text-anchor", "end") | |
| .style("fill", "#666") | |
| .text(function(d) { return d.name; }); | |
| } | |
| } | |
| var data = [{ | |
| "id": "usa", | |
| "country": "USA", | |
| "code": "US", | |
| "houses": [{ | |
| "id": "house", | |
| "name": "House", | |
| "totalMembers": 435, | |
| "parties": [ | |
| { | |
| "id": "d", | |
| "name": "Democrats", | |
| "totalMembers": 200 | |
| }, | |
| { | |
| "id": "r", | |
| "name": "Republicans", | |
| "totalMembers": 233 | |
| }, | |
| { | |
| "id": "vacant", | |
| "name": "Vacant", | |
| "totalMembers": 2 | |
| } | |
| ] | |
| }, | |
| { | |
| "id": "senate", | |
| "name": "Senate", | |
| "totalMembers": 100, | |
| "parties": [ | |
| { | |
| "id": "d", | |
| "name": "Democrats", | |
| "totalMembers": 53 | |
| }, | |
| { | |
| "id": "r", | |
| "name": "Republicans", | |
| "totalMembers": 45 | |
| }, | |
| { | |
| "id": "i", | |
| "name": "Independent", | |
| "totalMembers": 2 | |
| } | |
| ] | |
| }] | |
| }]; | |
| parliamentCharts(d3.select(".chart"), data); |
| body { | |
| background: #C1BBB8; | |
| } | |
| .chart { | |
| border: none; | |
| background: none; | |
| } | |
| .chart h3 { | |
| padding: 0 12px; | |
| } | |
| .house .house-members { | |
| font-size: 32px; | |
| font-weight: 500; | |
| } | |
| .house .house-name { | |
| font-size: 12px; | |
| font-weight: 100; | |
| text-transform: uppercase; | |
| letter-spacing: 1px; | |
| } |
Layout of US House & Senate members in an arc using D3. Based on code from http://en.wikipedia.org/wiki/User_talk:Slashme/parliament.py
A Pen by Liji Jinaraj on CodePen.