var screenWidth = document.documentElement.clientWidth; var screenHeight = document.documentElement.clientHeight; var maxWidth = 600; var preferredWidth = Math.min(600, screenWidth); var nestedData; var margin = {top: 30, right: 30, bottom: 10, left: 65}, width = preferredWidth - margin.left - margin.right, height = 2000 - margin.top - margin.bottom; var lineChartIndent = width/5; var lineChartWidth = width - lineChartIndent; const radius = preferredWidth > 200 ? 5 : 3; const opacity = {normal: 0.7, focus: 1, muted: 0.2, hidden: 0}; var currentChart = "bee"; var yScaleBee = d3.scaleLinear() .rangeRound([height/5, 0]); //TO CONVERT "5" TO FUNCTION var yScaleOutcome = d3.scaleBand() .range([height, 0]); var yAxisRight = d3.axisRight(yScaleBee).tickSize(1); var xScaleLine = d3.scaleLinear() .range([0, lineChartWidth]) .domain([0, 7]); //correct var colourOutcome = d3.scaleOrdinal() .range([siuColours.orange10, siuColours.teal10, siuColours.purple10, siuColours.green10, siuColours.greyDark]); var colourRegion = d3.scaleOrdinal() .range([siuColours.orange10, siuColours.teal10, siuColours.purple10, siuColours.green10, siuColours.greyDark]); var roiLine = d3.line() .x(function(d) { return xScaleLine(d.x); }) .y(function(d) { return yScaleBee(d.y); }) .curve(d3.curveLinear); var lineChartData; var svg = d3.select("#roi-chart") .append("svg") //.attr("viewBox", "0 0 "+ preferredWidth + " " + (height + margin.top + margin.bottom) ) //.attr("preserveAspectRatio", "xMidYMid meet"); .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .on("click", function() { resetChart(); }); var lineG = svg.append("g") .attr("transform", "translate(" + (margin.left + lineChartIndent) + "," + margin.top + ")"); var g = svg.append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); // LOAD DATA AND CREATE CHARTS d3.csv("roi4.csv", convertTextToNumbers, function(error, data) { if (error) throw error; // ENABLE THE DROP DOWN SELECTS var select = d3.selectAll("select"); select.on("change", function(d) { var selectedParents = d3.select("#no-of-parents").property("value"); var selectedChildren = d3.select("#no-of-children").property("value"); var selectedRegion = d3.select("#region").property("value"); var selectedIncome = d3.select("#income").property("value"); var selectedEthnicity = d3.select("#ethnicity").property("value"); highlightFamilies(selectedParents, selectedChildren, selectedRegion, selectedIncome, selectedEthnicity); }); //SET THE SCALES yScaleBee.domain([ Math.floor(d3.min(data, function(d) { return d.roi; } )), Math.ceil(d3.max(data, function(d) { return d.roi; } )), ]); yScaleOutcome.domain(data.sort(function(a, b) { return b.roi - a.roi; }) .map(function(d) { return d.outcome; }) ); colourOutcome.domain(yScaleBee.domain()); colourRegion.domain(data.sort(function(a, b) { return b.roi - a.roi; }) .map(function(d) { return d.region; }) ); lineChartData = d3.nest() .key(function(d) {return d.familyID;}) .key(function(d) {return d.outcome;}) .rollup(function(d) { var points = [] points = [ { x: 1, y: d3.mean(d, function(g) {return +g.year1;}) } , { x: 2, y: d3.mean(d, function(g) {return +g.year2;}) } , { x: 3, y: d3.mean(d, function(g) {return +g.year3;}) } , { x: 4, y: d3.mean(d, function(g) {return +g.year4;}) } , { x: 5, y: d3.mean(d, function(g) {return +g.year5;}) } , { x: 6, y: d3.mean(d, function(g) {return +g.year6;}) } ]; return points; }) .entries(data); var averageROI = d3.nest() .key(function(d) { return d.outcome; } ) .rollup(function(v) { return d3.mean(v, function(d) { return d.roi; } ); } ) .entries(data); // SET THE SCALES //xAxisTop.ticks( (yScaleBee.domain()[1] - yScaleBee.domain()[0] + 1) ); //xAxisBottom.ticks( (yScaleBee.domain()[1] - yScaleBee.domain()[0] + 1) ); // DRAW THE LABELS AND GRID LINES var yLabels = g.selectAll(".y-label") .data(yScaleOutcome.domain()) .enter() .append("text") .attr("class", "y-label") .attr("y", function(d) { return yScaleOutcome(d) + yScaleOutcome.bandwidth()/2; }) .attr("x", -10) .text(function(d){ return capitalizeFirstLetter(d); }); var yAxes = g.selectAll("axis-right") .data(averageROI) .enter() .append("g") .attr("class", "axis-right") .attr("transform", function(d) { return "translate("+ (width) +"," + yScaleOutcome(d.key) + ")" ; }) .call(yAxisRight); var gridLines = g.selectAll(".axis-right").selectAll(".tick") .append("line") .attr("class", "gridLine") .attr("x1", -lineChartWidth) .attr("y1", 0) .attr("x2", 0) .attr("y2", 0 ); var avgLines = g.selectAll(".avg-line") .data(averageROI) .enter() .append("g") .attr("class", "avg-line") .attr("transform", function(d) { return "translate(0," + ( yScaleOutcome(d.key) + yScaleBee(d.value) ) + ")"; }); avgLines.append("line") .attr("x1", lineChartIndent) .attr("x2", width) .attr("y1", 0) .attr("y2", 0); avgLines.append("text") .text(function(d) { return "avg: "+ oneDecimalPlace(d.value); } ) .attr("x", lineChartIndent - 10 ) .attr("y", "-0.2em" ); var FamilyLineCharts = lineG.selectAll(".g-family-line-charts") .data(lineChartData) .enter() .append("g") .attr("class", ".g-family-line-charts") .attr("id", function(d) { return "line-chart-" + d.key; } ); var outcomeGroups = FamilyLineCharts.selectAll(".g-outcome-lines") .data(function(d) {return d.values;} ) .enter() .append("g") .attr("class", "g-outcome-lines" ) .attr("transform", function(d) { return "translate(0,"+ yScaleOutcome(d.key) +")" } ); outcomeGroups.append('path') .datum(function(d) { return d.value; } ) .attr("d", roiLine) .style("opacity", opacity.hidden); // CREATE THE FORCE, TO LAYOUT THE CIRCLES - IE. SET THE X,Y COORDS. var simulation = d3.forceSimulation(data) .force("x", d3.forceX(xScaleLine(6) + lineChartIndent) ) .force("y", d3.forceY(function(d) { return yScaleOutcome(d.outcome) + yScaleBee(d.roi); })) .force("collide", d3.forceCollide(radius+1) ) //.on('tick', ticked) .stop(); for (var i = 0; i < 120; ++i) simulation.tick(); // CREATE THE VORONOI LAYOUT TO ENABLE EASIER SELECTION var voronoi = d3.voronoi() .extent([[-margin.left, -margin.top], [width + margin.right, height + margin.top]]) .x(function(d) { return d.x; }) .y(function(d) { return d.y; }); voronoiData = voronoi(data).polygons(); // DRAW THE THE CIRCLES var cell = g.append("g") .attr("class", "cells") .selectAll("g").data(voronoiData) .enter() .append("g") .attr("class", "polys"); cell.append("circle") .attr("r", radius) .attr("cx", function(d) { return d.data.x; }) .attr("cy", function(d) { return d.data.y; }) .style("fill", function(d) { return colourRegion(d.data.region); } ) .style("fill-opacity", opacity.normal ); var researchClip = g.append("defs").selectAll("clipPath") .data(voronoiData) .enter() .append("clipPath") .attr("id", function(d) { return "clip-research-" + d.data.id; }) .append("circle") .attr("cx", function(d) { return d.data.x; }) .attr("cy", function(d) { return d.data.y; }) .attr("r", radius + 20 ); cell.append("path") .attr("class", "clipped-paths") //.style("stroke", "grey") .attr("d", function(d) { return "M" + d.join("L") + "Z"; }) .attr("clip-path", function(d) { return "url(#clip-research-" + d.data.id + ")"; }) .on("click", showHideCircles); cell.append("title") .text(function(d) { return d.data.roi + "%" + "\nParents: " + d.data.parents + "\nChildren: " + d.data.children; }); }); // FUNCTIONS // RESET THE CIRCLE FORMATTING function resetChart() { setSelectValues("All", "All", "All", "All", "All"); resetLineCircles(); d3.selectAll(".clipped-paths") .each(function(d) { d.data.highlighted = false; } ); if(currentChart === "line") { d3.selectAll(".g-outcome-lines").selectAll("path") .transition() .duration(500) .style("opacity", opacity.muted) } }; function resetLineCircles() { d3.selectAll(".family-line") .style("stroke-opacity", opacity.hidden ); d3.selectAll(".polys") .selectAll("circle") .style("stroke", "none") .style("fill-opacity", opacity.normal ); if(currentChart === "line") { d3.selectAll(".g-outcome-lines").selectAll("path") .style("opacity", opacity.muted) } }; // handle the click function showHideCircles (d) { resetLineCircles(); if (d.data.highlighted) { d3.selectAll(".clipped-paths") .each(function(d) { d.data.highlighted = false; } ); setSelectValues("All", "All", "All", "All", "All"); } else { var thisFamily = d.data.familyID; console.log(d.data.ethnicity); d3.selectAll(".clipped-paths").filter(function(d) { return d.data.familyID === thisFamily; } ) .each(function(d) { d.data.highlighted = true; } ); d.data.highlighted = true; highlightFamily(d.data.familyID); setSelectValues(d.data.parents, d.data.children, d.data.region, d.data.income, d.data.ethnicity); }; d3.event.stopPropagation(); } ; // HIGHLIGHT ALL FAMILIES THAT MATCH THE SELECTED VALUES function highlightFamilies(noOfParents, noOfChildren, region, income, ethnicity) { var thisParents = noOfParents; var thisChildren = noOfChildren; var thisRegion = region; var thisIncome = income; var thisEthnicity = ethnicity; resetLineCircles(); if (thisParents !== "All" && thisChildren !== "All" && thisRegion !== "All" && thisIncome !== "All" && thisEthnicity !== "All") { var thisID = "" + thisParents + thisChildren + thisRegion + thisIncome + thisEthnicity; thisID = thisID.replace(/\s/g, ''); //remove white spaces highlightFamily(thisID); } else if (thisParents !== "All" || thisChildren !== "All" || thisRegion !== "All" || thisIncome !== "All" || thisEthnicity !== "All") { d3.selectAll(".polys") .selectAll("circle") .style("stroke", "none") .style("fill-opacity", opacity.muted); var filteredSelection = d3.selectAll(".polys") .selectAll("circle"); if (thisParents !== "All") { filteredSelection = filteredSelection.filter(function(d) { return d.data.parents == thisParents; }) }; if (thisChildren !== "All") { filteredSelection = filteredSelection.filter(function(d) { return d.data.children == thisChildren; }) }; if (thisRegion !== "All") { filteredSelection = filteredSelection.filter(function(d) { return d.data.region == thisRegion; }) }; if (thisIncome !== "All") { filteredSelection = filteredSelection.filter(function(d) { return d.data.income == thisIncome; }) }; if (thisEthnicity !== "All") { filteredSelection = filteredSelection.filter(function(d) { return d.data.ethnicity == thisEthnicity; }) }; filteredSelection .style("stroke", "black") .style("fill-opacity", opacity.focus) .each(function(d) { console.log("yyy"); if (currentChart === "line") { console.log(d.data.familyID); d3.selectAll("#line-chart-" + d.data.familyID).selectAll("path") .style("opacity", opacity.focus ); }; }); }; }; // end of function highlightFamilies // HIGHLIGHT A CERTAIN FAMILY TYPE function highlightFamily(selectedFamilyID) { var thisID = selectedFamilyID; d3.selectAll(".polys") .selectAll("circle") .style("stroke", function (d) { return d.data.familyID === thisID ? "black" : "none" ; }) .style("fill-opacity", function (d) { return d.data.familyID === thisID ? opacity.focus : opacity.muted ; }); d3.selectAll("#family-line-"+thisID) .style("stroke-opacity", opacity.focus ); if (currentChart === 'line' ) { d3.selectAll("#line-chart-" + thisID).selectAll("path") .style("opacity", opacity.focus ); }; }; // end of function highlightFamily // SET THE DROP DOWN VALUES BASED ON HIGHLIGHED CIRCLES function setSelectValues(parentToSelect, childrenToSelect, regionToSelect, incomeToSelect, ethnicityToSelect) { d3.select("#no-of-parents").property("value", parentToSelect); d3.select("#no-of-children").property("value", childrenToSelect); d3.select("#region").property("value", regionToSelect); d3.select("#income").property("value", incomeToSelect); d3.select("#ethnicity").property("value", ethnicityToSelect); }; function switchChart() { console.log("here"); resetLineCircles(); if (currentChart === "bee") { d3.selectAll(".polys") .selectAll("circle") .transition() .duration(500) .attr("cx", xScaleLine(6) + lineChartIndent) .attr("cy", function(d) { return yScaleBee(d.data.roi) + yScaleOutcome(d.data.outcome); }); d3.selectAll(".g-outcome-lines").selectAll("path") .transition() .duration(500) .style("opacity", opacity.muted) currentChart = "line"; } else { d3.selectAll(".g-outcome-lines").selectAll("path") .transition() .duration(500) .style("opacity", opacity.hidden) d3.selectAll(".polys") .selectAll("circle") .transition() .duration(500) .attr("cx", function(d) { return d.data.x; }) .attr("cy", function(d) { return d.data.y; }); currentChart = "bee"; }; }; //CONVERT CSV TEXT TO NUMBERS function convertTextToNumbers(d) { d.id = +d.id; d.parents = +d.parents; d.children = +d.children; d.roi = +d.roi; d.year1 = +d.year1; d.year2 = +d.year2; d.year3 = +d.year3; d.year4 = +d.year4; d.year5 = +d.year5; d.year6 = +d.year6; return d; }; // RESIZE LISTENER addEvent(window, "resize", function(event) { console.log('resized'); screenWidth = document.documentElement.clientWidth; screenHeight = document.documentElement.clientHeight; console.log("w: " + screenWidth + " h: " + screenHeight ); preferredWidth = Math.min(600, screenWidth); console.log("p: " + preferredWidth); });