D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
ElaineYu
Full window
Github gist
Metis Health & Wealth of Nations Increment Year
Built with
blockbuilder.org
<!DOCTYPE html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="description" content="Skeleton file to explore data in Class 3"> <title>Data Reading & Shaping</title> <style> .axisText { font: 20px Helvetica, Verdana, Tahoma; } #counter-year { width: 300px; height: 100%; float: left; margin-top: 10px; margin-left: 65px; border-style: solid; } #counter { font: bold 50px Helvetica, Verdana, Tahoma; margin: 15px; float: right; } input { font: bold, Helvetica, Verdana, Tahoma; font-size: 15px; background-color: aquamarine; margin-top: 30px; margin-left: 15px; float:left; } div.tooltip { position: absolute; pointer-events: none; background: radial-gradient( 5px -9px, circle, white 8%, red 26px ); background-color: red; border: 2px solid white; border-radius: 12px; /* one half of ( (border * 2) + height + padding ) */ box-shadow: 1px 1px 1px black; color: white; font: bold 15px/13px Helvetica, Verdana, Tahoma; height: 16px; min-width: 14px; padding: 4px 3px 0 3px; text-align: center; } </style> </head> <body> <div id="counter-year"> <input type="button" value="Increment Year" onclick="updateData()" /> <h4 id="counter"></h4> <div> <script src="https://d3js.org/d3.v4.min.js"></script> <script> // Define the margin object with properties for the four sides var margin = {top: 20, right: 40, bottom: 200, left: 70}; // define width and height as the INNER dimensions of the chart area var width = 960 - margin.left - margin.right; var height = 500 - margin.top - margin.bottom; var svg = d3.select("body") .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") // define svg as a G element that translates the origin to the top-left corner of the chart area. .attr("transform", "translate(" + margin.left + "," + margin.top + ")") //(translate (20, 20) var endPoint = {}; var scale = {}; var findByYear = function(data, year) { var resultArry = data.find(function(i) { return i[0] === year }) if(resultArry) { return resultArry[1] } } var buildNationForEachYear = function(nation, year) { var n = { name: nation.name, region: nation.region, income: findByYear(nation.income, year), population: findByYear(nation.population, year), lifeExpectancy: findByYear(nation.lifeExpectancy, year) } return n } var loadDomain = function(xValues, yValues, popValues, dataForYearX) { return { x: d3.extent(xValues), y: d3.extent(yValues), pop: d3.extent(popValues), color: d3.nest().key(function(nation) {return nation.region}).object(dataForYearX) } } var loadRange = function() { return { x: [0 , width], y: [height, 0], pop: [0, 100], color: ['red', 'orange', 'green', 'cyan', 'blue', 'purple'] } } var loadScale = function(domain, range){ return { x: d3.scaleLog().domain(domain.x).range(range.x), y: d3.scaleLinear().domain(domain.y).range(range.y), pop: d3.scaleSqrt().domain(domain.pop).range(range.pop), color: d3.scaleOrdinal().domain(domain.color).range(range.color) } } var loadAxis = function(scale) { return { x: d3.axisBottom().scale(scale.x).ticks(20, ",.1s"), y: d3.axisLeft().scale(scale.y) } } var appendAxes = function(svg, axes){ svg.append('g') .attr("transform", "translate(0," + height + ")") // Move x-axis down translate(tx, ty) / (0, 500 - margin.top - margin.bottom) .call(axes.x) svg .append('g') .call(axes.y) return svg } var appendXYLabels = function(svg) { svg.append("text") .attr("class", "axisText") .attr("transform", "rotate(-90)") .attr("y", 0 - margin.left) .attr("x",0 - (height / 2)) .attr("dy", "1em") .style("text-anchor", "middle") .text("Life Expectancy"); svg.append("text") .attr("class", "axisText") .attr("transform", "translate(" + (width/2) + " ," + (height + margin.top + 20) + ")") .style("text-anchor", "middle") .text("Income per capita"); } var year = 1950; function updateBubblesByYear(svg, data, scale) { // Initial year // Increment year for data year += 1; if (year > 2008) { year = 1950; } // Set display counter document.getElementById("counter").innerHTML = year; var bubbles = svg.selectAll('circle') .data(data[year], nation => nation.name) // key function: controls which datum is assigned to which element // exit. Get rid of data to match the selection bubbles.exit().remove(); // enter selection. Chain attributes that don't depend on data. Create a circle for every placeholder var enter = bubbles.enter() .append('circle') .on("mouseover", function(d) { div.transition() .duration(200) .style("opacity", .9); div.html(d.name) .style("left", (d3.event.pageX) + "px") .style("top", (d3.event.pageY - 28) + "px"); }) .on("mouseout", function(d) { div.transition() .duration(500) .style("opacity", 0); }); // enter + update. Combines 2 selections into one. This recomputes the data join and maintains the desired correspondence between elements and data. Chain attrs dependent on data bubbles = enter.merge(bubbles) .transition(d3.transition().duration(1000)) .attr("cx", function(nation) { return scale.x(nation.income) }) .attr("cy", function(nation) { return scale.y(nation.lifeExpectancy); }) .attr("r", function(nation) { return scale.pop(nation.population || 1)}) .style("opacity", .2) // set the element opacity .style("stroke", "red") // set the line colour .style("fill", function(nation) {return scale.color(nation.region)}) } var div = d3.select("body").append("div") .attr("class", "tooltip") .style("opacity", 0); function getYearsExtentForAllCategories(nations) { return nations.map(function(nation) { // Determine Min and Max for each var incomeMinMax = d3.extent(nation.income, function(i) {return i[0]}) // [1800, 2009] var lifeMinMax = d3.extent(nation.lifeExpectancy, function(i) {return i[0]}) // [1803, 2000] var popMinMax = d3.extent(nation.population, function(i) {return i[0]}) // [1900, 2001] // Merge the arrays var allMinMax = d3.merge([incomeMinMax, lifeMinMax, popMinMax]) return d3.extent(allMinMax) }) } d3.json('nations.json', function(error, nations) { console.log('Original data'); console.log(nations); // make an object where keys are years in the data with an empty array to put our nation data for that year var allIncomeValues = []; var allLifeExpectancyValues = []; var allPopulationValues = []; var nationYears = getYearsExtentForAllCategories(nations); var yearMinMax = d3.extent(d3.merge(nationYears)); for(var year=yearMinMax[0]; year<=yearMinMax[1]; ++year) { endPoint[year] = []; nations.forEach(function(nation) { var newNation = buildNationForEachYear(nation, year) if (newNation.income) { allIncomeValues.push(newNation.income); } if (newNation.lifeExpectancy){ allLifeExpectancyValues.push(newNation.lifeExpectancy); } if (newNation.population){ allPopulationValues.push(newNation.population); } // Remove undefined values if (newNation.income && newNation.lifeExpectancy && newNation.population) { endPoint[year].push(newNation); } }) } // Newly structured data console.log('endPoint', endPoint); var year = 1800; newYear = year++; var domain = loadDomain(allIncomeValues, allLifeExpectancyValues, allPopulationValues, endPoint[newYear]); var range = loadRange(); scale = loadScale(domain, range); var axes = loadAxis(scale) appendAxes(svg, axes); appendXYLabels(svg); updateBubblesByYear(svg, endPoint, scale); }); function updateData() { updateBubblesByYear(svg, endPoint, scale); } </script> </body>
https://d3js.org/d3.v4.min.js