chart = {
var background_color = "#f2f3f4"
var unicef_color = "green"
var wfp_color = "blue"
var highlight_color = "#D79922"
var outline_color = "#4056A1"
var center_circle_color = "#C5CBE3"
var federal_color = "#f13C20"
var font_color = '#303C6C'
var fontsize = d3.scaleSqrt()
.domain([0, 150])
.range([1, 20])
.clamp(true)
const svg = d3.create("svg")
.attr("viewBox", `-${width / 2} -${height*1.15 /(2)} ${width} ${height}`)
.style("display", "block")
.style("background",background_color)
.style("cursor", "pointer")
const toolTip = d3.select("body")
.append("div")
.attr("class", "toolTip")
.style("position", "absolute")
.style("display", "none")
.style("border-radius", "5px")
.style("height", "auto")
.style("background", "white")
.style("padding", "7px")
.style("border", "1px solid #DDD")
.style("font-size", "1.1rem")
.style("text-align", "left")
//https://stackoverflow.com/questions/149055/how-to-format-numbers-as-currency-strings
// Create our number formatter.
var formatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
// These options are needed to round to whole numbers if that's what you want.
//minimumFractionDigits: 0, // (this suffices for whole numbers, but will print 2500.10 as $2,500.1)
// maximumFractionDigits: 0, // (causes 2500.99 to be printed as $2,501)
});
// apple mouseovers
function leafMouseOver(event, d){
if (d.depth == 1) {
d3.select(event.target).attr("stroke-width", 4)
d3.select(event.target).attr("stroke", highlight_color)
d3.select(event.target)
var formated_money =parseFloat(d.data.outlay)*parseFloat(dd3)/8992408699023
var formated_money = formatter.format(formated_money)
toolTip.style("left", event.pageX + 18 + "px")
.style("top", event.pageY + 18 + "px")
.style("display", "block")
.html(`
Double click to zoom into this circle<br><br>
<b>${d.data.name}</b> <br>
Your taxes provide : <strong> ${formated_money} </strong>`)
}
else if (groupByPriority === "Level3") {
d3.select(event.target).attr("stroke-width", 4)
d3.select(event.target).attr("stroke", highlight_color)
var formattedString = String(d.data.award_description.join(",<br/><br/>-"))
var formated_money =parseFloat(d.data.outlay)*parseFloat(dd3)/8992408699023
formated_money = formatter.format(formated_money)
toolTip.style("left", event.pageX + 18 + "px")
.style("top", event.pageY + 18 + "px")
.style("display", "block")
.html(`
Double click to zoom into this circle<br><br>
<b>Award Recipiant</b> : ${d.data.name} <br>
<b>Your taxes provide</b> : ${formated_money} <br>
<b> Top award descriptions</b> : ${formattedString}<br><br>
<b>Part of:</b> ${d.parent.data.name}`);
}
else {
d3.select(event.target).attr("stroke-width", 4)
d3.select(event.target).attr("stroke", highlight_color)
var formated_money =parseFloat(d.data.outlay)*parseFloat(dd3)/8992408699023
formated_money = formatter.format(formated_money)
toolTip.style("left", event.pageX + 18 + "px")
.style("top", event.pageY + 18 + "px")
.style("display", "block")
.html(`
Double click to zoom into this circle<br><br>
<b> ${d.data.name}</b> <br>
<b> Your taxes provide :</b> <strong> ${formated_money} </strong> <br><br>
<b>Part of:</b> ${d.parent.data.name}`);
}
}
function leafMouseOut(event, d){
d3.select(event.target).attr("stroke", d => d.data.column_name == "federal_account_name" ? federal_color: (d.data.name == "International Affairs" || d.data.name == "Agency For International Development") ? highlight_color : outline_color)
d3.select(event.target).attr("stroke-width",d =>(d.data.name == "International Affairs" || d.data.name == "Agency for International Development") ? 5 : d => d.data.un_money == "no"? 5: 1)
toolTip.style("display", "none");
}
function setYear(level) {
function zoomTo(v) {
toolTip.style("display", "none");
k = width / v[2];
node
.attr("transform", d => `translate(${(d.x - v[0]) * k},${(d.y - v[1]) * k})`)
.attr("r", d => d.r * k);
label.attr("transform", d => `translate(${(d.x - v[0]) * k},${(d.y - v[1]) * k})`)
.attr("font-size", d => fontsize(d.r * k))
.style("fill-opacity", function(d) { if (fontsize(d.r * k) > 6 && d.parent == focus) {return 1} else {return 0} })
.text(function (d) {
return d.data.name.substring(0, 20) + "...";
})
}
function zoom(d) {
console.log(d)
if (d.height ==0)
{
focus = d.parent}
else {focus = d}
zoomTo([focus.x, focus.y, focus.r * 2])
if (d.parent )
{ if (d.children) {
zoom_title.text(d.data.name.substring(0, 30) + "...")}
else {zoom_title.text(d.parent.data.name.substring(0, 30) + "...")}}
else {zoom_title.text("")}
}
var variable = (level =="Level1" && step == "1") ?
{"data": account_function, "opacity_true": true, "opacity_name": "International Affairs", "zoom_in": false, "zoom_name": "International Affairs", "text_title1": "The budget passed by Congress assigns funds to Federal Accounts", "text_title2": "Most of the humanitarian aid falls under the budget function 'International Affairs'", "text_title3": " ", "text_title1_color": true, "text_title2_color": false, "text_title3_color": false, "head_title": "All Budget Functions and Federal Accounts", "starting_opacity": 0}
: (level =="Level1" && step == "2") ?
{"data": account_function, "opacity_true": true, "opacity_name": "International Affairs", "zoom_in": true, "zoom_name": "International Affairs", "text_title1": "The budget passed by Congress assigns funds to Federal Accounts", "text_title2": "Most of the humanitarian aid falls under the budget function 'International Affairs'", "text_title3": " ", "text_title1_color": false, "text_title2_color": true, "text_title3_color": false, "head_title": "All Budget Functions and Federal Accounts", "starting_opacity": .8}
: (level =="Level1" && step == "3") ?
{"data": account_function, "opacity_true": true, "opacity_name": "International Affairs", "zoom_in": false, "zoom_name": "International Affairs", "text_title1": "The budget passed by Congress assigns funds to Federal Accounts", "text_title2": "Most of the humanitarian aid falls under the budget function 'International Affairs'", "text_title3": " ", "text_title1_color": false, "text_title2_color": true, "text_title3_color": true, "head_title": "All Budget Functions and Federal Accounts", "starting_opacity": .8}
:(level =="Level2" && step == "1") ?
{"data": account_agency, "opacity_true": true, "opacity_name": "Agency For International Development", "zoom_in": false, "zoom_name": "Agency For International Development", "text_title1": "Federal Accounts are controlled by one or more Federal Agency", "text_title2": "The Agency For International Development controls many of the accounts for humanitarian aid", "text_title3": " ", "text_title1_color": true, "text_title2_color": false, "text_title3_color": false, "text_title": "Federal Accounts are controlled by one or more Federal Agency", "head_title": "All Federal Agencies and Federal Accounts", "starting_opacity": 0}
: (level =="Level2" && step == "2") ?
{"data": account_agency, "opacity_true": true, "opacity_name": "Agency For International Development", "zoom_in": true, "zoom_name": "Agency For International Development", "text_title1": "Federal Accounts are controlled by one or more Federal Agency", "text_title2": "The Agency For International Development controls many of the accounts for humanitarian aid", "text_title3": " ", "text_title1_color": false, "text_title2_color": true, "text_title3_color": false, "head_title": "All Federal Agencies and Federal Accounts", "starting_opacity": .8}
: (level =="Level2" && step == "3") ?
{"data": account_agency, "opacity_true": true, "opacity_name": "Agency for International Development", "zoom_in": false, "zoom_name": "Agency for International Development", "text_title1": "Federal Accounts are controlled by one or more Federal Agency", "text_title2": "The Agency For International Development controls many of the accounts for humanitarian aid", "text_title3": " ", "text_title1_color": false, "text_title2_color": true, "text_title3_color": true, "head_title": "All Federal Agencies and Federal Accounts", "starting_opacity": .8}
:(level =="Level3" && step == "1") ?
{"data": account_recipients, "opacity_true": true, "opacity_name": "Agency for International Development", "zoom_in": false, "zoom_name": "Agency for International Development", "text_title1": "The agencies (biggest circle) decide how to allocate funds to recipients", "text_title2": "These Federal Accounts (bold orange circles) are a small faction of the total Federal Accounts", "text_title3": "Double click the smallest circle to see recipients. WFP is in blue, UNICEF is green", "text_title1_color": true, "text_title2_color": false, "text_title3_color": false, "head_title": "Federal Accounts that give 1 cent of average tax bill to UNICEF or WFP", "starting_opacity": 0}
: (level =="Level3" && step == "2") ?
{"data": account_agency, "opacity_true": false, "opacity_name": "Agency For International Development", "zoom_in": false, "zoom_name": "Agency For International Development", "text_title1": "The agencies (biggest circle) decide how to allocate funds to recipients", "text_title2": "These Federal Accounts ( bold orange circles) are a small faction of the total Federal Accounts", "text_title3": "Double click the smallest circle to see recipients. WFP is in blue, UNICEF is green", "text_title1_color": false, "text_title2_color": true, "text_title3_color": false, "head_title": "All Federal Agencies and Federal Accounts", "starting_opacity": 0}
:
{"data": account_recipients, "opacity_true": true, "opacity_name": "Agency for International Development", "zoom_in": false, "zoom_name": "International Disaster Assistance, Funds Appropriated To The President, Us Agency For International Development", "text_title1": "The agencies (biggest circle) decide how to allocate funds to recipients", "text_title2": "These Federal Accounts (bold orange circles) are a small faction of the total Federal Accounts", "text_title3": "Double click the smallest circle to see recipients. WFP is in blue, UNICEF is green", "text_title1_color": false, "text_title2_color": false, "text_title3_color": true, "head_title": "Federal Accounts that give 1 cent of average tax bill to UNICEF or WFP", "starting_opacity": .8}
var root = pack(variable.data)
var opacity_true = variable.opacity_true
var opacity_name = variable.opacity_name
var zoom_in = variable.zoom_in
var zoom_name = variable.zoom_name
var starting_opacity = variable.starting_opacity
var head_title = variable.head_title
var text_title1 = variable.text_title1
var text_title2 = variable.text_title2
var text_title3 = variable.text_title3
var text_title1_color = variable.text_title1_color
var text_title2_color = variable.text_title2_color
var text_title3_color = variable.text_title3_color
var focus = root
var v = [root.x, root.y, root.r * 2]
let format = d3.format(",d")
let current_circle = undefined;
var k = 1
var node = svg.append("g")
.selectAll("circle")
.data(root.descendants().slice(1))
var label = svg.append("g")
.selectAll("text")
.data(root.descendants().slice(1))
svg
.on("dblclick", (event) => zoom( root));
node = node
.join(
enter => enter.append('circle')
.attr("transform", d => `translate(${(d.x - v[0]) * k},${(d.y - v[1]) * k})`)
.attr("r", d => d.r * k)
//if there are still children, color by how far in you are
.attr("stroke", d => d.data.column_name == "federal_account_name" ? highlight_color: (d.data.name == "International Affairs" || d.data.name == "Agency For International Development" ) ? highlight_color : outline_color)
.attr("stroke-width", d => (d.data.name == "International Affairs" || d.data.name == "Agency For International Development") ? 5 : 2)
.attr("fill", d => d.children ? background_color: d.data.name =="World Food Program" ? wfp_color: d.data.name =="Unicef" ? unicef_color: center_circle_color)
.attr("opacity", starting_opacity)
.attr("class", d=> d.data.name == zoom_name ? "pick_me": "never_mind")
.on("dblclick", (event, d) => focus !== d && (zoom(d), event.stopPropagation()))
.on("mouseover", (event, d) => leafMouseOver(event, d))
.on("mouseout", (event, d) => leafMouseOut(event, d)),
)
node
// .transition()
// .duration(1000)
.attr('opacity', d => opacity_true ? .7 : (d.data.un_money == "yes" ) ? .2 : 1)
label = label
.join(
enter => enter.append('text')
.attr("pointer-events", "none")
.attr("text-anchor", "middle")
.attr("transform", d => `translate(${(d.x - v[0]) * k},${(d.y - v[1]) * k})`)
.attr("opacity", starting_opacity)
//label resized using new radius
.attr("font-size", d => fontsize(d.r * k))
.attr('font-weight', 700)
.style("fill", font_color)
.style("fill-opacity", function(d) { if (fontsize(d.r * k) > 6 && d.depth == 1) {return 1} else {return 0} })
.text(function (d) {
return d.data.name.substring(0, 20) + "...";
})
)
label
.attr('opacity', d => opacity_true ? 1 : (d.data.un_money == "yes" ) ? 1 : 0)
// tooltip + styling
svg.append('rect')
.attr('x', -width / 2)
.attr('y', -height*1.15 /(2))
.attr('width', width)
.attr('height', height/9)
.attr('stroke', 'black')
.attr('fill', 'white');
var title1 = svg.append("text")
.attr("x", 0)
.attr("y", -height/2 -25)
.attr("text-anchor", "middle")
.style("font-size", "20px")
title1.text(text_title1)
.attr('font-weight', text_title1_color? 700: 500)
var title2 = svg.append("text")
.attr("x", 0)
.attr("y", -height/2)
.attr("text-anchor", "middle")
.style("font-size", "20px")
console.log(text_title1_color, text_title2_color, text_title3_color)
title2.text(text_title2)
.attr('font-weight', text_title2_color? 700: 500)
var title3 = svg.append("text")
.attr("x", 0)
.attr("y", -height/2 + 25)
.attr("text-anchor", "middle")
.style("font-size", "20px")
.style('fill', text_title3_color)
title3.text(text_title3)
.attr('font-weight', text_title3_color? 700: 500)
var first_title = svg.append("text")
.attr("x", 0)
.attr("y", -height/2 - 50)
.attr("text-anchor", "middle")
.style("font-size", "22px")
.style("text-decoration", "underline")
first_title.text(head_title)
var second_title = svg.append("text")
.attr("x", 0)
.attr("y", -height/2 + 60)
.attr("text-anchor", "middle")
.style("font-size", "20px")
second_title.text("Double click in circles to zoom in. Double click outside circles to zoom back out")
var second_title = svg.append("text")
.attr("x", 0)
.attr("y", -height/2 + 80)
.attr("text-anchor", "middle")
.style("font-size", "20px")
second_title.text("Hover for more information")
var zoom_title = svg.append("text")
.attr("x", 0)
.attr("y", -height/2 + 110)
.attr("text-anchor", "middle")
.style("font-size", "30px")
.attr('font-weight', 700)
d3.selectAll('circle.pick_me').attr("stroke", highlight_color)
d3.selectAll('circle.pick_me').filter(function(d, i) { return i == 0 }).select(function(d) {zoom_in ? zoom(d): console.log("nope")})
}
return Object.assign(svg.node(), { setYear });
}