Lantz McGinnis-Brown
  • About
  • Research
  • Teaching & Training
  • Blog
  • CV
Idaho Nonprofit Dashboard
  • Map & Overview
  • Activities & Classifications
  • Organizational Exemption Years
  • Affiliation & Organization Type
  • Assets, Income, & Revenue
  • Tables
L = {
  const L = await require("leaflet/dist/leaflet.js");
  if (!L._style) {
    const href = await require.resolve("leaflet/dist/leaflet.css");
    document.head.appendChild(L._style = html`<link href=${href} rel=stylesheet>`);
  }

  return L;
};
data = transpose(raw_data);
class_data = transpose(raw_class_data);
map_data = transpose(raw_map_data);
summary_data = transpose(raw_sum_data);
class_sum = transpose(sum_class_data);
year_counts = transpose(year_count_data);
affiliations = transpose(raw_affiliations);
org_type = transpose(raw_org_type);
assets = transpose(raw_assets);
income = transpose(raw_income); 
finances = transpose(raw_finances);

filterOptions = [...new Set(data.map(d => d.NTEE_Core_Description))];

filteredData = {
  if (selectedGroup === "" || selectedGroup === "all") {
    return data;
  } else {
    return data.filter(d => d.NTEE_Core_Description === selectedGroup);
  }
}

filteredAffiliations = {
  if (selectedGroup === "" || selectedGroup === "all") {
    return affiliations;
  } else {
    return affiliations.filter(d => d.NTEE_Core_Description === selectedGroup);
  }
}

filteredOrgType = {
  if (selectedGroup === "" || selectedGroup === "all") {
    return org_type;
  } else {
    return org_type.filter(d => d.NTEE_Core_Description === selectedGroup);
  }
}

filteredAssets = {
  if (selectedGroup === "" || selectedGroup === "all") {
    return assets;
  } else {
    return assets.filter(d => d.NTEE_Core_Description === selectedGroup);
  }
}

filteredIncome = {
  if (selectedGroup === "" || selectedGroup === "all") {
    return income;
  } else {
    return income.filter(d => d.NTEE_Core_Description === selectedGroup);
  }
}

filteredFinances = {
  if (selectedGroup === "" || selectedGroup === "all") {
    return finances;
  } else {
    return finances.filter(d => d.NTEE_Core_Description === selectedGroup);
  }
}

filteredClassData = {
 if (selectedGroup === "" || selectedGroup === "all") {
    return class_data;
  } else {
    return class_data.filter(d => d.NTEE_Core_Description === selectedGroup);
  }
}

filteredYearCounts = {
if (selectedGroup === "" || selectedGroup == "all"){
  return year_counts
      //.filter(d => d.Ruling_Year >= 1900)
      .filter(d => d.Ruling_Year < 2025);
} else {
    return year_counts
    .filter(d => d.NTEE_Core_Description === selectedGroup)
    //.filter(d => d.Ruling_Year >= 1900)
    .filter(d => d.Ruling_Year < 2025);
}
}
html`<label for="ntee-select">Choose an NTEE Activity Filter:</label>`
viewof selectedGroup = html`<select id="ntee-select">
  <option value="All">All</option>
  ${filterOptions.map(option => `<option value="${option}">${option}</option>`)}
</select>
`
html`
<em style = "font-size: 20px;">Choose an NTEE activity from the dropdown menu to filter results by that activity.</em>
`
Idaho Nonprofit Map
container = {

  let x = d3.create("div")
  
  x.attr("style", `width:100%;height:${window.outerHeight * 0.69}px`);

  return x.node();

};

map = L.map(container, {zoomSnap: 0.25});

tilelayer = L.tileLayer('https://basemap.nationalmap.gov/arcgis/rest/services/USGSTopo/MapServer/tile/{z}/{y}/{x}', {
    attribution: 'Tiles courtesy of the <a href="https://usgs.gov/">U.S. Geological Survey</a>', maxZoom: 20
    }).addTo(map);
  
markerLayerGroup = L.layerGroup().addTo(map); 

markerIcon = L.icon({
  iconUrl: `https://raw.githubusercontent.com/primer/octicons/ede8e1acef3c7c036799af286f1ee2d755f04225/icons/info-24.svg`,
  iconSize: [31, 46],
  popupAnchor: [0, -10]
});

//setTimeout(() => {
reactiveMapUpdate = { 
 markerLayerGroup.clearLayers(); 

  const filteredLocations = selectedGroup === "" || selectedGroup === "All"
    ? map_data
    : map_data.filter(d => d.NTEE_Core_Description === selectedGroup);
    
  const defaultMarker = L.icon({
    iconUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png',
    shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',
    iconAnchor:   [12, 41],
    shadowAnchor: [4, 62],
    popupAnchor:  [-3, -76]
  });

  filteredLocations.forEach(location => {
    L.marker([location.lat, location.long], {icon: defaultMarker})
      .bindTooltip(`<b>${location.NAME}</b><br>In Care of: ${location.ICO}<br>NTEE Code: ${location.NTEE_Core_Description}<br><em>Full NTEE Description: ${location.NTEE_Full_Description}</em>`)
      .addTo(markerLayerGroup); // Add markers to the layer group
  });
    
  map.setView([44.8582, -113.7420], 6.25);
  
  setTimeout(() => {
    L.marker([47.9, -109.9], {icon: markerIcon}).addTo(map)
    .bindPopup('All data in this dashboard comes from<br>Idaho IRS 990 Extract Data')
    .openPopup();
}, 50);
  
  return undefined;
};
  • Nonprofits by Activity
  • Nonprofits by Sub-Activity (Filtered)
  • Assets
  • Assets by Sub-Activity (Filtered)
{

summary_data.sort((a,b) => b.Count - a.Count);

const initialWidth = 600; // Initial width for viewBox
const initialHeight = 500; // Initial height for viewBox

const margin = {top: 25, right: 30, bottom: 40, left: 300};
//const width = 1000 - margin.left - margin.right;
//const height = 500 - margin.top - margin.bottom;

var svg = d3.create("svg")
    .attr("viewBox", [0, 0, initialWidth, initialHeight]) // Set the viewBox
    .attr("width", "100%") // Make it responsive width-wise
    .attr("height", "100%"); // Make it responsive height-wise
    //.attr("width", width + margin.left + margin.right)
    //.attr("height", height + margin.top + margin.bottom);

var x = d3.scaleLinear()
  .domain([0, d3.max(summary_data, d => d.Count)])
  .range([0, initialWidth - margin.left - margin.right]);

var y = d3.scaleBand()
  .range([0, initialHeight - margin.top - margin.bottom])
  .domain(summary_data.map(d => d.NTEE_Core_Description))
  .padding(0.1);

var plotGroup = svg.append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

// Corrected x-axis placement
plotGroup.append("g")
  .attr("class", "xAxis")
  .attr("transform", `translate(0,${initialHeight - margin.top - margin.bottom})`) // Place at the bottom
  .call(d3.axisBottom(x));

plotGroup.append("g")
  .attr("class", "yAxis")
  .call(d3.axisLeft(y))
  .selectAll("text")
  .style("text-anchor", "end");

const bars = plotGroup.selectAll("rect")
  .data(summary_data)
  .join(
  enter => enter.append("rect")
  .attr("x", x(0)+1)
  .attr("y", initialHeight)
  .attr("width", function(d) { return x(d.Count); })
  .attr("height", 0) // Corrected height calculation
  .attr("fill", "#69B3A2")
  .call(enter => enter.transition()
    .duration(500)
    .attr("y", function(d) { return y(d.NTEE_Core_Description); })
    .attr("height", y.bandwidth())),
  update => update
    .call(update => update.transition()
      .duration(500)
      .attr("x", x(0)+1)
      .attr("y", function(d) { return y(d.NTEE_Core_Description); })
      .attr("width", function(d) { return x(d.Count); })
      .attr("height", y.bandwidth())),
  exit => exit
    .call(exit => exit.transition()
      .duration(500)
      .attr("y", initialHeight)
      .attr("height", 0)
      .remove()) //left off here - some kind of error
);

svg.append("text")
  .attr("x", margin.left)
  .attr("y", margin.top - 7)
  .attr("text-anchor", "middle")
  .attr("class", "chart-title")
  .text("# of Nonprofits by Activity");
  
let tooltip = d3.select("body").select(".tooltip");
if (tooltip.empty()) {
  tooltip = d3.select("body")
    .append("div")
    .attr("class", "tooltip")
    .style("opacity", 0)
    .style("position", "absolute")
    .style("background-color", "white")
    .style("border", "1px solid black")
    .style("padding", "10px")
    .style("pointer-events", "none"); // To prevent blocking mouse events
}

bars.on("mouseover", function(event, d) {
    tooltip.transition()
      .duration(200)
      .style("opacity", 0.9);
    tooltip.html(`Activity: ${d.NTEE_Core_Description}<br>Count: ${d.Count}`) // Customize tooltip content
      .style("left", (event.pageX + 10) + "px")
      .style("top", (event.pageY - 28) + "px");
  })
  .on("mousemove", function(event) {
    tooltip.style("left", (event.pageX + 10) + "px")
      .style("top", (event.pageY - 28) + "px");
  })
  .on("mouseout", function() {
    tooltip.transition()
      .duration(500)
      .style("opacity", 0);
  });

return svg.node();
}
{

filteredData.sort((a,b) => b.Count - a.Count);

const columnValues = filteredData.map(d => d.NTEE_Full_Description);

const uniqueValues = [...new Set(columnValues)];

const uniqueCount = uniqueValues.length;

const initialWidth = 600; // Initial width for viewBox
const initialHeight =  uniqueCount * 15;// Initial height for viewBox

const margin = {top: 25, right: 30, bottom: 40, left: 300};
//const width = 1000 - margin.left - margin.right;
//const height = 500 - margin.top - margin.bottom;

var svg = d3.create("svg")
    .attr("viewBox", [0, 0, initialWidth, initialHeight]) // Set the viewBox
    .attr("width", "100%") // Make it responsive width-wise
    .attr("height", "100%"); // Make it responsive height-wise
    //.attr("width", width + margin.left + margin.right)
    //.attr("height", height + margin.top + margin.bottom);

var x = d3.scaleLinear()
  .domain([0, d3.max(filteredData, d => d.Count)])
  .range([0, initialWidth - margin.left - margin.right]);

var y = d3.scaleBand()
  .range([0, initialHeight - margin.top - margin.bottom])
  .domain(filteredData.map(d => d.NTEE_Full_Description))
  .padding(0.1);

var plotGroup = svg.append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

// Corrected x-axis placement
plotGroup.append("g")
  .attr("class", "xAxis")
  .attr("transform", `translate(0,${initialHeight - margin.top - margin.bottom})`) // Place at the bottom
  .call(d3.axisBottom(x));

plotGroup.append("g")
  .attr("class", "yAxis")
  .call(d3.axisLeft(y))
  .selectAll("text")
  .style("text-anchor", "end");

const bars = plotGroup.selectAll("rect")
  .data(filteredData)
  .join(
  enter => enter.append("rect")
  .attr("x", x(0)+1)
  .attr("y", initialHeight)
  .attr("width", function(d) { return x(d.Count); })
  .attr("height", 0) // Corrected height calculation
  .attr("fill", "#69B3A2")
  .call(enter => enter.transition()
    .duration(500)
    .attr("y", function(d) { return y(d.NTEE_Full_Description); })
    .attr("height", y.bandwidth())),
  update => update
    .call(update => update.transition()
      .duration(500)
      .attr("x", x(0)+1)
      .attr("y", function(d) { return y(d.NTEE_Full_Description); })
      .attr("width", function(d) { return x(d.Count); })
      .attr("height", y.bandwidth())),
  exit => exit
    .call(exit => exit.transition()
      .duration(500)
      .attr("y", initialHeight)
      .attr("height", 0)
      .remove()) //left off here - some kind of error
);

svg.append("text")
  .attr("x", margin.left)
  .attr("y", margin.top - 7)
  .attr("text-anchor", "middle")
  .attr("class", "chart-title");
  
svg.selectAll(".chart-title")
  .data([selectedGroup])
  .join("text")
    .text(d => d ? `Sub-activities among ${d} Nonprofits`: "All Categories");
    
let tooltip = d3.select("body").select(".tooltip");
if (tooltip.empty()) {
  tooltip = d3.select("body")
    .append("div")
    .attr("class", "tooltip")
    .style("opacity", 0)
    .style("position", "absolute")
    .style("background-color", "white")
    .style("border", "1px solid black")
    .style("padding", "10px")
    .style("pointer-events", "none"); // To prevent blocking mouse events
}

bars.on("mouseover", function(event, d) {
    tooltip.transition()
      .duration(200)
      .style("opacity", 0.9);
    tooltip.html(`Sub-Activity: ${d.NTEE_Full_Description}<br>Count: ${d.Count}`) // Customize tooltip content
      .style("left", (event.pageX + 10) + "px")
      .style("top", (event.pageY - 28) + "px");
  })
  .on("mousemove", function(event) {
    tooltip.style("left", (event.pageX + 10) + "px")
      .style("top", (event.pageY - 28) + "px");
  })
  .on("mouseout", function() {
    tooltip.transition()
      .duration(500)
      .style("opacity", 0);
  });
  
  
return svg.node();
}
{

summary_data.sort((a,b) => b.Perc_of_Assets - a.Perc_of_Assets);

const initialWidth = 600; // Initial width for viewBox
const initialHeight = 500; // Initial height for viewBox

const margin = {top: 25, right: 30, bottom: 40, left: 300};
//const width = 1000 - margin.left - margin.right;
//const height = 500 - margin.top - margin.bottom;

var svg = d3.create("svg")
    .attr("viewBox", [0, 0, initialWidth, initialHeight]) // Set the viewBox
    .attr("width", "100%") // Make it responsive width-wise
    .attr("height", "100%"); // Make it responsive height-wise
    //.attr("width", width + margin.left + margin.right)
    //.attr("height", height + margin.top + margin.bottom);

var x = d3.scaleLinear()
  .domain([0, d3.max(summary_data, d => d.Perc_of_Assets)])
  .range([0, initialWidth - margin.left - margin.right]);

var y = d3.scaleBand()
  .range([0, initialHeight - margin.top - margin.bottom])
  .domain(summary_data.map(d => d.NTEE_Core_Description))
  .padding(0.1);

var plotGroup = svg.append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

// Corrected x-axis placement
plotGroup.append("g")
  .attr("class", "xAxis")
  .attr("transform", `translate(0,${initialHeight - margin.top - margin.bottom})`) // Place at the bottom
  .call(d3.axisBottom(x)
          .tickFormat(d3.format(".0%")));

plotGroup.append("g")
  .attr("class", "yAxis")
  .call(d3.axisLeft(y))
  .selectAll("text")
  .style("text-anchor", "end");

const bars = plotGroup.selectAll("rect")
  .data(summary_data)
  .join(
  enter => enter.append("rect")
  .attr("x", x(0)+1)
  .attr("y", initialHeight)
  .attr("width", function(d) { return x(d.Perc_of_Assets); })
  .attr("height", 0) // Corrected height calculation
  .attr("fill", "#69B3A2")
  .call(enter => enter.transition()
    .duration(500)
    .attr("y", function(d) { return y(d.NTEE_Core_Description); })
    .attr("height", y.bandwidth())),
  update => update
    .call(update => update.transition()
      .duration(500)
      .attr("x", x(0)+1)
      .attr("y", function(d) { return y(d.NTEE_Core_Description); })
      .attr("width", function(d) { return x(d.Perc_of_Assets); })
      .attr("height", y.bandwidth())),
  exit => exit
    .call(exit => exit.transition()
      .duration(500)
      .attr("y", initialHeight)
      .attr("height", 0)
      .remove()) //left off here - some kind of error
);

svg.append("text")
  .attr("x", margin.left)
  .attr("y", margin.top - 7)
  .attr("text-anchor", "middle")
  .attr("class", "chart-title")
  .text("% of Nonprofit Sector Assets by Activity");
  
let tooltip = d3.select("body").select(".tooltip");
if (tooltip.empty()) {
  tooltip = d3.select("body")
    .append("div")
    .attr("class", "tooltip")
    .style("opacity", 0)
    .style("position", "absolute")
    .style("background-color", "white")
    .style("border", "1px solid black")
    .style("padding", "10px")
    .style("pointer-events", "none"); // To prevent blocking mouse events
}

const formatter = d3.format(".1%");

bars.on("mouseover", function(event, d) {
    tooltip.transition()
      .duration(200)
      .style("opacity", 0.9);
    tooltip.html(`Activity: ${d.NTEE_Core_Description}<br>% of Assets: ${formatter(d.Perc_of_Assets)}`) // Customize tooltip content
      .style("left", (event.pageX + 10) + "px")
      .style("top", (event.pageY - 28) + "px");
  })
  .on("mousemove", function(event) {
    tooltip.style("left", (event.pageX + 10) + "px")
      .style("top", (event.pageY - 28) + "px");
  })
  .on("mouseout", function() {
    tooltip.transition()
      .duration(500)
      .style("opacity", 0);
  });

return svg.node();
}
{

filteredData.sort((a,b) => b.Core_Assets - a.Core_Assets)
            .filter(d => d.Perc_of_Assets > 0.0);

const columnValues = filteredData.map(d => d.NTEE_Full_Description);

const uniqueValues = [...new Set(columnValues)];

const uniqueCount = uniqueValues.length;

const initialWidth = 600; // Initial width for viewBox
const initialHeight = uniqueCount * 15; // Initial height for viewBox

const margin = {top: 25, right: 30, bottom: 40, left: 300};
//const width = 1000 - margin.left - margin.right;
//const height = 500 - margin.top - margin.bottom;

var svg = d3.create("svg")
    .attr("viewBox", [0, 0, initialWidth, initialHeight]) // Set the viewBox
    .attr("width", "100%") // Make it responsive width-wise
    .attr("height", "100%"); // Make it responsive height-wise
    //.attr("width", width + margin.left + margin.right)
    //.attr("height", height + margin.top + margin.bottom);

var x = d3.scaleLinear()
  .domain([0, d3.max(filteredData, d => d.Core_Assets)])
  .range([0, initialWidth - margin.left - margin.right]);

var y = d3.scaleBand()
  .range([0, initialHeight - margin.top - margin.bottom])
  .domain(filteredData.map(d => d.NTEE_Full_Description))
  .padding(0.1);

var plotGroup = svg.append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

// Corrected x-axis placement
plotGroup.append("g")
  .attr("class", "xAxis")
  .attr("transform", `translate(0,${initialHeight - margin.top - margin.bottom})`) // Place at the bottom
  .call(d3.axisBottom(x)
          .tickFormat(d3.format("$,.2s")));

plotGroup.append("g")
  .attr("class", "yAxis")
  .call(d3.axisLeft(y))
  .selectAll("text")
  .style("text-anchor", "end");

const bars = plotGroup.selectAll("rect")
  .data(filteredData)
  .join(
  enter => enter.append("rect")
  .attr("x", x(0)+1)
  .attr("y", initialHeight)
  .attr("width", function(d) { return x(d.Core_Assets); })
  .attr("height", 0) // Corrected height calculation
  .attr("fill", "#69B3A2")
  .call(enter => enter.transition()
    .duration(500)
    .attr("y", function(d) { return y(d.NTEE_Full_Description); })
    .attr("height", y.bandwidth())),
  update => update
    .call(update => update.transition()
      .duration(500)
      .attr("x", x(0)+1)
      .attr("y", function(d) { return y(d.NTEE_Full_Description); })
      .attr("width", function(d) { return x(d.Core_Assets); })
      .attr("height", y.bandwidth())),
  exit => exit
    .call(exit => exit.transition()
      .duration(500)
      .attr("y", initialHeight)
      .attr("height", 0)
      .remove()) //left off here - some kind of error
);

svg.append("text")
  .attr("x", margin.left)
  .attr("y", margin.top - 7)
  .attr("text-anchor", "middle")
  .attr("class", "chart-title");
  
svg.selectAll(".chart-title")
  .data([selectedGroup])
  .join("text")
    .text(d => d ? `Total Assets by Sub-Activity among ${d} Nonprofits`: "All Categories");
  
let tooltip = d3.select("body").select(".tooltip");
if (tooltip.empty()) {
  tooltip = d3.select("body")
    .append("div")
    .attr("class", "tooltip")
    .style("opacity", 0)
    .style("position", "absolute")
    .style("background-color", "white")
    .style("border", "1px solid black")
    .style("padding", "10px")
    .style("pointer-events", "none"); // To prevent blocking mouse events
}

const formatter = d3.format("$,.3s")

bars.on("mouseover", function(event, d) {
    tooltip.transition()
      .duration(200)
      .style("opacity", 0.9);
    tooltip.html(`Activity: ${d.NTEE_Full_Description}<br>Total Assets: ${formatter(d.Core_Assets)}`) // Customize tooltip content
      .style("left", (event.pageX + 10) + "px")
      .style("top", (event.pageY - 28) + "px");
  })
  .on("mousemove", function(event) {
    tooltip.style("left", (event.pageX + 10) + "px")
      .style("top", (event.pageY - 28) + "px");
  })
  .on("mouseout", function() {
    tooltip.transition()
      .duration(500)
      .style("opacity", 0);
  });
  
return svg.node();
}
  • Idaho Nonprofit Classifications
  • Classifications by Activity (Filtered)
{

const initialWidth = 600; // Initial width for viewBox
const initialHeight = 500; // Initial height for viewBox

const margin = {top: 25, right: 30, bottom: 40, left: 300};
//const width = 1000 - margin.left - margin.right;
//const height = 500 - margin.top - margin.bottom;

var svg = d3.create("svg")
    .attr("viewBox", [0, 0, initialWidth, initialHeight]) // Set the viewBox
    .attr("width", "100%") // Make it responsive width-wise
    .attr("height", "100%"); // Make it responsive height-wise
    //.attr("width", width + margin.left + margin.right)
    //.attr("height", height + margin.top + margin.bottom);

var x = d3.scaleLinear()
  .domain([0, d3.max(class_sum, d => d.Count)])
  .range([0, initialWidth - margin.left - margin.right]);

var y = d3.scaleBand()
  .range([0, initialHeight - margin.top - margin.bottom])
  .domain(class_sum.map(d => d.Classification_Type))
  .padding(0.1);

var plotGroup = svg.append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

// Corrected x-axis placement
plotGroup.append("g")
  .attr("class", "xAxis")
  .attr("transform", `translate(0,${initialHeight - margin.top - margin.bottom})`) // Place at the bottom
  .call(d3.axisBottom(x));

plotGroup.append("g")
  .attr("class", "yAxis")
  .call(d3.axisLeft(y))
  .selectAll("text")
  .style("text-anchor", "end");

const bars = plotGroup.selectAll("rect")
  .data(class_sum)
  .join(
  enter => enter.append("rect")
  .attr("x", x(0)+1)
  .attr("y", initialHeight)
  .attr("width", function(d) { return x(d.Count); })
  .attr("height", 0) // Corrected height calculation
  .attr("fill", "#69B3A2")
  .call(enter => enter.transition()
    .duration(500)
    .attr("y", function(d) { return y(d.Classification_Type); })
    .attr("height", y.bandwidth())),
  update => update
    .call(update => update.transition()
      .duration(500)
      .attr("x", x(0)+1)
      .attr("y", function(d) { return y(d.Classification_Type); })
      .attr("width", function(d) { return x(d.Count); })
      .attr("height", y.bandwidth())),
  exit => exit
    .call(exit => exit.transition()
      .duration(500)
      .attr("y", initialHeight)
      .attr("height", 0)
      .remove()) //left off here - some kind of error
);

svg.append("text")
  .attr("x", margin.left)
  .attr("y", margin.top - 7)
  .attr("text-anchor", "middle")
  .attr("class", "chart-title")
  .text("Idaho Nonprofit Classifications");
  
let tooltip = d3.select("body").select(".tooltip");
if (tooltip.empty()) {
  tooltip = d3.select("body")
    .append("div")
    .attr("class", "tooltip")
    .style("opacity", 0)
    .style("position", "absolute")
    .style("background-color", "white")
    .style("border", "1px solid black")
    .style("padding", "10px")
    .style("pointer-events", "none"); // To prevent blocking mouse events
}

bars.on("mouseover", function(event, d) {
    tooltip.transition()
      .duration(200)
      .style("opacity", 0.9);
    tooltip.html(`Classification: ${d.Classification_Type}<br>Count: ${d.Count}`) // Customize tooltip content
      .style("left", (event.pageX + 10) + "px")
      .style("top", (event.pageY - 28) + "px");
  })
  .on("mousemove", function(event) {
    tooltip.style("left", (event.pageX + 10) + "px")
      .style("top", (event.pageY - 28) + "px");
  })
  .on("mouseout", function() {
    tooltip.transition()
      .duration(500)
      .style("opacity", 0);
  });

  
  
return svg.node();
}
{

const initialWidth = 600; // Initial width for viewBox
const initialHeight = 500; // Initial height for viewBox

const margin = {top: 25, right: 30, bottom: 40, left: 300};
//const width = 1000 - margin.left - margin.right;
//const height = 500 - margin.top - margin.bottom;

var svg = d3.create("svg")
    .attr("viewBox", [0, 0, initialWidth, initialHeight]) // Set the viewBox
    .attr("width", "100%") // Make it responsive width-wise
    .attr("height", "100%"); // Make it responsive height-wise
    //.attr("width", width + margin.left + margin.right)
    //.attr("height", height + margin.top + margin.bottom);

var x = d3.scaleLinear()
  .domain([0, d3.max(filteredClassData, d => d.Count)])
  .range([0, initialWidth - margin.left - margin.right]);

var y = d3.scaleBand()
  .range([0, initialHeight - margin.top - margin.bottom])
  .domain(filteredClassData.map(d => d.Classification_Type))
  .padding(0.1);

var plotGroup = svg.append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

// Corrected x-axis placement
plotGroup.append("g")
  .attr("class", "xAxis")
  .attr("transform", `translate(0,${initialHeight - margin.top - margin.bottom})`) // Place at the bottom
  .call(d3.axisBottom(x));

plotGroup.append("g")
  .attr("class", "yAxis")
  .call(d3.axisLeft(y))
  .selectAll("text")
  .style("text-anchor", "end");

const bars = plotGroup.selectAll("rect")
  .data(filteredClassData)
  .join(
  enter => enter.append("rect")
  .attr("x", x(0)+1)
  .attr("y", initialHeight)
  .attr("width", function(d) { return x(d.Count); })
  .attr("height", 0) // Corrected height calculation
  .attr("fill", "#69B3A2")
  .call(enter => enter.transition()
    .duration(500)
    .attr("y", function(d) { return y(d.Classification_Type); })
    .attr("height", y.bandwidth())),
  update => update
    .call(update => update.transition()
      .duration(500)
      .attr("x", x(0)+1)
      .attr("y", function(d) { return y(d.Classification_Type); })
      .attr("width", function(d) { return x(d.Count); })
      .attr("height", y.bandwidth())),
  exit => exit
    .call(exit => exit.transition()
      .duration(500)
      .attr("y", initialHeight)
      .attr("height", 0)
      .remove()) //left off here - some kind of error
);

svg.append("text")
  .attr("x", margin.left)
  .attr("y", margin.top - 7)
  .attr("text-anchor", "middle")
  .attr("class", "chart-title");
  
svg.selectAll(".chart-title")
  .data([selectedGroup])
  .join("text")
    .text(d => d ? `Classifications of ${d} Nonprofits`: "All Categories");
    
let tooltip = d3.select("body").select(".tooltip");
if (tooltip.empty()) {
  tooltip = d3.select("body")
    .append("div")
    .attr("class", "tooltip")
    .style("opacity", 0)
    .style("position", "absolute")
    .style("background-color", "white")
    .style("border", "1px solid black")
    .style("padding", "10px")
    .style("pointer-events", "none"); // To prevent blocking mouse events
}

bars.on("mouseover", function(event, d) {
    tooltip.transition()
      .duration(200)
      .style("opacity", 0.9);
    tooltip.html(`Classification: ${d.Classification_Type}<br>Count: ${d.Count}`) // Customize tooltip content
      .style("left", (event.pageX + 10) + "px")
      .style("top", (event.pageY - 28) + "px");
  })
  .on("mousemove", function(event) {
    tooltip.style("left", (event.pageX + 10) + "px")
      .style("top", (event.pageY - 28) + "px");
  })
  .on("mouseout", function() {
    tooltip.transition()
      .duration(500)
      .style("opacity", 0);
  });
  
  
return svg.node();
}
Plot.plot({
//title: "When organizations were ruled to be exempt...",
marginLeft: 65,
marginTop: 55,
marginBottom: 85,
  marks: [
  Plot.barY(
  filteredYearCounts,
  {
  x: "Ruling_Year",
  y: "count",
  title: d=> `${d.count} nonprofits were ruled exempt in ${d.Ruling_Year}`
}),
  Plot.text([`When organizations were first ruled to be exempt...`], {frameAnchor: "top-left", dy: -40, textAnchor: "start", fontWeight: "bold", fontSize: "20", fontFamily: "Gotham"})
] ,
  x: {
  label: "Year Ruled Exempt",
  tickFormat: (d) => String(Number(d)),
  tickRotate: -90
},
  y: {
  label: ""
}
})
Plot.plot({
marginLeft: 550,
marginTop: 55,
marginBottom: 100,
  marks: [
  Plot.barX(
  filteredAffiliations,
  {
  x: "count",
  y: "Affiliation_Coded",
  title: d=> `${d.count} nonprofits are affiliated as ${d.Affiliation_Coded}`,
  sort: {y: "x", reverse: true}
}),
 Plot.text([`Affiliation Status`], {frameAnchor: "top-left", dy: -40, textAnchor: "start", fontWeight: "bold", fontSize: "24", fontFamily: "Gotham"})
] ,
  x: {
  label: "Number of Organizations",
  //tickFormat: (d) => String(Number(d)),
  tickRotate: -90
},
  y: {
  label: "Affiliation Category"
}
})
Plot.plot({
marginLeft: 220,
marginTop: 55,
marginBottom: 100,
  marks: [
  Plot.barX(
  filteredOrgType,
  {
  x: "count",
  y: "Organization_Coded",
  title: d=> `${d.count} nonprofits are classified as ${d.Organization_Coded}`,
  sort: {y: "x", reverse: true}
}),
 Plot.text([`Organization Type`], {frameAnchor: "top-left", dy: -40, textAnchor: "start", fontWeight: "bold", fontSize: "24", fontFamily: "Gotham"})
] ,
  x: {
  label: "Number of Organizations",
  tickRotate: -90
},
  y: {
  label: "Organization Type"
}
})
viewof selectedZero = Inputs.toggle({label: "Exclude Zeros?", values: ["Yes", "No"]});
filteredAssets2 = {
 if (selectedZero === "No") {
    return filteredAssets;
  } else {
    return filteredAssets.filter(d => d.Assets_Coded != "$0");
  }
}

filteredIncome2 = {
 if (selectedZero === "No") {
    return filteredIncome;
  } else {
    return filteredIncome.filter(d => d.Income_Coded != "$0");
  }
}

filteredFinances2 = {
 if (selectedZero === "No") {
    return filteredFinances;
  } else {
    return filteredFinances.filter(d => d.Revenue_Coded != "$0");
  }
}
{
  const height = Math.min(width, 750);
  const radius = Math.min(width, height) / 2;
  const marginTop = 60;
  const marginLeft = 40;
  const minAngle = 0.15;

  const arc = d3.arc()
      .innerRadius(radius * 0.67)
      .outerRadius(radius - 1);

  const pie = d3.pie()
      .padAngle(1 / radius)
      .sort(null)
      .value(d => d.count);

  const color = d3.scaleOrdinal()
      .domain(filteredAssets2.map(d => d.Assets_Coded))
      .range(d3.quantize(t => d3.interpolateSpectral(t * 0.8 + 0.1), filteredAssets2.length).reverse());

  const svg = d3.create("svg")
      //.attr("width", width)
      .attr("height", height)
      .attr("viewBox", [-radius - marginLeft, -radius - marginTop, 2 * radius + marginLeft + 40, 2 * radius + marginTop])
      .attr("style", "max-width: 100%; height: auto;");
      
  const tooltip = d3.select("body")
    .append("div")
    .attr("class", "tooltip")
    .style("opacity", 0)
    .style("position", "absolute")
    .style("background-color", "white")
    .style("border", "1px solid #ccc")
    .style("padding", "8px")
    .style("border-radius", "4px")
    .style("font-size", "14px");

  svg.append("g")
    .selectAll()
    .data(pie(filteredAssets2))
    .join("path")
      .attr("fill", d => color(d.data.Assets_Coded))
      .attr("d", arc)
       .on("mouseover", function(event, d) { 
        tooltip.transition()
          .duration(200)
          .style("opacity", 0.9);
        tooltip.html(`${d.data.count.toLocaleString()} orgs have ${d.data.Assets_Coded} assets`)
          .style("left", (event.pageX + 10) + "px")
          .style("top", (event.pageY - 28) + "px");
        d3.select(this)
          .style("opacity", 0.8); 
      })
      .on("mouseout", function(event, d) { 
        tooltip.transition()
          .duration(500)
          .style("opacity", 0);
        d3.select(this)
          .style("opacity", 1); 
      });
   // .append("title")
   //   .text(d => `${d.data.count.toLocaleString()} orgs have ${d.data.Assets_Coded} assets`);
      
  const textGroup = svg.append("g")
      .attr("font-family", "Gotham")
      .attr("font-size", 14)
      .attr("text-anchor", "middle")
    .selectAll()
    .data(pie(filteredAssets2))
    .join("g")
    .attr("transform", d => {
        const centroid = arc.centroid(d);
        const x = centroid[0];
        const y = centroid[1];
        const scaleFactor = 0.84;
        const newX = x * scaleFactor;
        const newY = y * scaleFactor;
        return `translate(${newX},${newY})`;
      })
      .filter(d => (d.endAngle - d.startAngle) > minAngle)
      .each(function(d) { 
        const centroid = arc.centroid(d);
        const x = centroid[0];
        const y = centroid[1];
        const angle = Math.atan2(y, x) * 180 / Math.PI;
        const adjustedAngle = (angle > 90 || angle < -90) ? angle + 180 : angle;
        d3.select(this)
          .attr("transform", `${this.getAttribute("transform")} rotate(${adjustedAngle})`)
          .attr("text-anchor", (d) => (x > 0) ? "start" : "end");
      });
    
textGroup.append("text")
 .attr("y", "-0.4em")
          .attr("font-weight", "bold")
          .text(d => d.data.Assets_Coded);
          
textGroup.append("text")
 .attr("x", 0)
          .attr("y", "0.7em")
          .attr("fill-opacity", 1)
          .text(d => `${d.data.count.toLocaleString()} orgs`);

  svg.append("text")
      .attr("text-anchor", "middle")
      .attr("dominant-baseline", "central")
      .style("font-family", "Gotham")
      .style("font-size", "2em")
      .style("font-weight", "bold")
      .text("Assets");

  return svg.node();
}
{
  const height = Math.min(width, 750);
  const radius = Math.min(width, height) / 2;
  const marginTop = 60;
  const marginLeft = 40;
  const minAngle = 0.15;

  const arc = d3.arc()
      .innerRadius(radius * 0.67)
      .outerRadius(radius - 1);

  const pie = d3.pie()
      .padAngle(1 / radius)
      .sort(null)
      .value(d => d.count);

  const color = d3.scaleOrdinal()
      .domain(filteredIncome2.map(d => d.Income_Coded))
      .range(d3.quantize(t => d3.interpolateSpectral(t * 0.8 + 0.1), filteredIncome2.length).reverse());

  const svg = d3.create("svg")
      //.attr("width", width)
      .attr("height", height)
      .attr("viewBox", [-radius - marginLeft, -radius - marginTop, 2 * radius + marginLeft + 40, 2 * radius + marginTop])
      .attr("style", "max-width: 100%; height: auto;");
      
  const tooltip = d3.select("body")
    .append("div")
    .attr("class", "tooltip")
    .style("opacity", 0)
    .style("position", "absolute")
    .style("background-color", "white")
    .style("border", "1px solid #ccc")
    .style("padding", "8px")
    .style("border-radius", "4px")
    .style("font-size", "14px");

  svg.append("g")
    .selectAll()
    .data(pie(filteredIncome2))
    .join("path")
      .attr("fill", d => color(d.data.Income_Coded))
      .attr("d", arc)
       .on("mouseover", function(event, d) { 
        tooltip.transition()
          .duration(200)
          .style("opacity", 0.9);
        tooltip.html(`${d.data.count.toLocaleString()} orgs have ${d.data.Income_Coded} income`)
          .style("left", (event.pageX + 10) + "px")
          .style("top", (event.pageY - 28) + "px");
        d3.select(this)
          .style("opacity", 0.8); 
      })
      .on("mouseout", function(event, d) { 
        tooltip.transition()
          .duration(500)
          .style("opacity", 0);
        d3.select(this)
          .style("opacity", 1); 
      });
   // .append("title")
   //   .text(d => `${d.data.count.toLocaleString()} orgs have ${d.data.Assets_Coded} assets`);
      
  const textGroup = svg.append("g")
      .attr("font-family", "Gotham")
      .attr("font-size", 14)
      .attr("text-anchor", "middle")
    .selectAll()
    .data(pie(filteredIncome2))
    .join("g")
    .attr("transform", d => {
        const centroid = arc.centroid(d);
        const x = centroid[0];
        const y = centroid[1];
        const scaleFactor = 0.84;
        const newX = x * scaleFactor;
        const newY = y * scaleFactor;
        return `translate(${newX},${newY})`;
      })
      .filter(d => (d.endAngle - d.startAngle) > minAngle)
      .each(function(d) { 
        const centroid = arc.centroid(d);
        const x = centroid[0];
        const y = centroid[1];
        const angle = Math.atan2(y, x) * 180 / Math.PI;
        const adjustedAngle = (angle > 90 || angle < -90) ? angle + 180 : angle;
        d3.select(this)
          .attr("transform", `${this.getAttribute("transform")} rotate(${adjustedAngle})`)
          .attr("text-anchor", (d) => (x > 0) ? "start" : "end");
      });
    
textGroup.append("text")
 .attr("y", "-0.4em")
          .attr("font-weight", "bold")
          .text(d => d.data.Income_Coded);
          
textGroup.append("text")
 .attr("x", 0)
          .attr("y", "0.7em")
          .attr("fill-opacity", 1)
          .text(d => `${d.data.count.toLocaleString()} orgs`);
          
  svg.append("text")
      .attr("text-anchor", "middle")
      .attr("dominant-baseline", "central")
      .style("font-family", "Gotham")
      .style("font-size", "2em")
      .style("font-weight", "bold")
      .text("Income");



  return svg.node();
}
{
  const height = Math.min(width, 750);
  const radius = Math.min(width, height) / 2;
  const marginTop = 60;
  const marginLeft = 40;
  const minAngle = 0.15;
  const chartTitle = "Revenue";

  const arc = d3.arc()
      .innerRadius(radius * 0.67)
      .outerRadius(radius - 1);

  const pie = d3.pie()
      .padAngle(1 / radius)
      .sort(null)
      .value(d => d.count);

  const color = d3.scaleOrdinal()
      .domain(filteredFinances2.map(d => d.Revenue_Coded))
      .range(d3.quantize(t => d3.interpolateSpectral(t * 0.8 + 0.1), filteredFinances2.length).reverse());

  const svg = d3.create("svg")
    //  .attr("width", width)
      .attr("height", height)
      .attr("viewBox", [-radius - marginLeft, -radius - marginTop, 2 * radius + marginLeft + 40, 2 * radius + marginTop])
      .attr("style", "max-width: 100%; height: auto;");
      
  const tooltip = d3.select("body")
    .append("div")
    .attr("class", "tooltip")
    .style("opacity", 0)
    .style("position", "absolute")
    .style("background-color", "white")
    .style("border", "1px solid #ccc")
    .style("padding", "8px")
    .style("border-radius", "4px")
    .style("font-size", "14px");

  svg.append("g")
    .selectAll()
    .data(pie(filteredFinances2))
    .join("path")
      .attr("fill", d => color(d.data.Revenue_Coded))
      .attr("d", arc)
       .on("mouseover", function(event, d) { 
        tooltip.transition()
          .duration(200)
          .style("opacity", 0.9);
        tooltip.html(`${d.data.count.toLocaleString()} orgs have ${d.data.Revenue_Coded} revenue`)
          .style("left", (event.pageX + 10) + "px")
          .style("top", (event.pageY - 28) + "px");
        d3.select(this)
          .style("opacity", 0.8); 
      })
      .on("mouseout", function(event, d) { 
        tooltip.transition()
          .duration(500)
          .style("opacity", 0);
        d3.select(this)
          .style("opacity", 1); 
      });
   // .append("title")
   //   .text(d => `${d.data.count.toLocaleString()} orgs have ${d.data.Assets_Coded} assets`);
      
  const textGroup = svg.append("g")
      .attr("font-family", "Gotham")
      .attr("font-size", 14)
      .attr("text-anchor", "middle")
    .selectAll()
    .data(pie(filteredFinances2))
    .join("g")
    .attr("transform", d => {
        const centroid = arc.centroid(d);
        const x = centroid[0];
        const y = centroid[1];
        const scaleFactor = 0.84;
        const newX = x * scaleFactor;
        const newY = y * scaleFactor;
        return `translate(${newX},${newY})`;
      })
      .filter(d => (d.endAngle - d.startAngle) > minAngle)
      .each(function(d) { 
        const centroid = arc.centroid(d);
        const x = centroid[0];
        const y = centroid[1];
        const angle = Math.atan2(y, x) * 180 / Math.PI;
        const adjustedAngle = (angle > 90 || angle < -90) ? angle + 180 : angle;
        d3.select(this)
          .attr("transform", `${this.getAttribute("transform")} rotate(${adjustedAngle})`)
          .attr("text-anchor", (d) => (x > 0) ? "start" : "end");
      });
    
textGroup.append("text")
 .attr("y", "-0.4em")
          .attr("font-weight", "bold")
          .text(d => d.data.Revenue_Coded);
          
textGroup.append("text")
 .attr("x", 0)
          .attr("y", "0.7em")
          .attr("fill-opacity", 1)
          .text(d => `${d.data.count.toLocaleString()} orgs`);
          
  svg.append("text")
      .attr("text-anchor", "middle")
      .attr("dominant-baseline", "central")
      .style("font-family", "Gotham")
      .style("font-size", "2em")
      .style("font-weight", "bold")
      .text(chartTitle);



  return svg.node();
}
  • Financial Summary Table
  • The Full Data
{
if (selectedGroup === "All"){
  Reactable.setFilter(
  `summary_tbl`,
  `Activity`,
  undefined)
} else {
Reactable.setFilter(
  'summary_tbl',
  'Activity',
  selectedGroup
);
}

}
{
if (selectedGroup === "All"){
  Reactable.setFilter(
  `full_tbl`,
  `Activity`,
  undefined)
} else {
Reactable.setFilter(
  'full_tbl',
  'Activity',
  selectedGroup
);
}

}