diff --git a/src/components/Beeswarm.vue b/src/components/Beeswarm.vue index fb34291a154cb4e5397128a2bb554c13b3b83c8c..a78da4dc7892d6dae51f4462fe4604de3a820f0a 100644 --- a/src/components/Beeswarm.vue +++ b/src/components/Beeswarm.vue @@ -12,7 +12,7 @@ > <p v-html="agVsMunText.paragraph1" /> </div> - <div + <!-- <div id="toggle-container" class="text-container" aria-hidden="true" @@ -51,6 +51,20 @@ >Indicator</label> <span class="graph-buttons-switch-selection" /> </div> + </div> --> + <div id="toggle-container" > + <div id="checkbox1-container"> + <input type="checkbox" id="checkbox1" v-model="checkboxStates[0]" @change="handleCheckboxChange(0)"> + <label for="checkbox1">Evidence</label> + </div> + <div id="checkbox2-container"> + <input type="checkbox" id="checkbox2" v-model="checkboxStates[1]" @change="handleCheckboxChange(1)"> + <label for="checkbox2">Agreement</label> + </div> + <div id="checkbox3-container"> + <input type="checkbox" id="checkbox3" v-model="checkboxStates[2]" @change="handleCheckboxChange(2)"> + <label for="checkbox3">Direction</label> + </div> </div> <div id="beeswarm-chart-container"></div> <!-- <div id="beeswarm-chart-container"></div> --> @@ -96,33 +110,40 @@ const dataSet1 = ref([]); const dataSet2 = ref([]); const selectedDataSet = ref('dataSet1'); + const checkboxStates = ref([false, false, false]); + const checkboxFunctions = [sizeByEvidence, positionByAgreement, sortByDirection]; + console.log(checkboxStates.value[0]); + console.log(checkboxStates.value[1]); + console.log(checkboxStates.value[2]); + const categoryCenters = {} let data = dataSet1; let simulation; + + // set up svg let svg; - const height = 600; const width = 1000; let margin = {top: 100, right: 20, bottom: 20, left: 40} // set colors for bubble charts const dimensionColors = { - Demographiccharacteristics: "#625D0B", - Landtenure: "#5C0601", - Livingconditions: "#0B4E8B", - Socioeconomicstatus: "#DC8260", - Health: "#7F4A89", - Riskperception: "#249CB1", - Exposure: "#B47D83" - } + Demographiccharacteristics: "#625D0B", + Landtenure: "#5C0601", + Livingconditions: "#0B4E8B", + Socioeconomicstatus: "#DC8260", + Health: "#7F4A89", + Riskperception: "#249CB1", + Exposure: "#B47D83" + } // load data and then make chart onMounted(() => { console.log("component mounted"); loadDatasets().then(() => { if (selectedDataSet.value.length > 0) { - addToggle() - // createBubbleChart(selectedDataSet); - createBeeswarmChart(selectedDataSet); + // addToggle() + createBubbleChart(selectedDataSet); + // createBeeswarmChart(selectedDataSet); } else { console.error('Error loading data:', error) } @@ -145,40 +166,40 @@ }); }; - function addToggle() { - const toggleContainer = document.getElementById('toggle-container'); - const toggleLabels = toggleContainer.querySelectorAll('.graph-buttons-switch-label'); - const selectionIndicator = toggleContainer.querySelector('.graph-buttons-switch-selection'); - - toggleLabels.forEach(label => { - label.addEventListener('click', () => { - // Remove active class from all labels - toggleLabels.forEach(l => l.classList.remove('active')); - // Add active class to the clicked label - label.classList.add('active'); - - // Update selection indicator position - const rect = label.getBoundingClientRect(); - selectionIndicator.style.left = `${rect.left - toggleContainer.getBoundingClientRect().left}px`; - - // Update selectedDataSet value based on the clicked label - selectedDataSet.value = label.getAttribute('for'); - // Run functions based on selected dataset - if (label.getAttribute('for') === 'id_dataSet1') { - resetBubbles(dataSet1); - } else if (label.getAttribute('for') === 'id_dataSet2') { - categorizeBubbles(dataSet2); - } - }); - }); - - // Set initial position of selection indicator - const defaultLabel = toggleContainer.querySelector('.graph-buttons-switch-label.active'); - if (defaultLabel) { - const rect = defaultLabel.getBoundingClientRect(); - selectionIndicator.style.left = `${rect.left - toggleContainer.getBoundingClientRect().left}px`; - } - } + // function addToggle() { + // const toggleContainer = document.getElementById('toggle-container'); + // const toggleLabels = toggleContainer.querySelectorAll('.graph-buttons-switch-label'); + // const selectionIndicator = toggleContainer.querySelector('.graph-buttons-switch-selection'); + + // toggleLabels.forEach(label => { + // label.addEventListener('click', () => { + // // Remove active class from all labels + // toggleLabels.forEach(l => l.classList.remove('active')); + // // Add active class to the clicked label + // label.classList.add('active'); + + // // Update selection indicator position + // const rect = label.getBoundingClientRect(); + // selectionIndicator.style.left = `${rect.left - toggleContainer.getBoundingClientRect().left}px`; + + // // Update selectedDataSet value based on the clicked label + // selectedDataSet.value = label.getAttribute('for'); + // // Run functions based on selected dataset + // if (label.getAttribute('for') === 'id_dataSet1') { + // resetBubbles(dataSet1); + // } else if (label.getAttribute('for') === 'id_dataSet2') { + // categorizeBubbles(dataSet2); + // } + // }); + // }); + + // // Set initial position of selection indicator + // const defaultLabel = toggleContainer.querySelector('.graph-buttons-switch-label.active'); + // if (defaultLabel) { + // const rect = defaultLabel.getBoundingClientRect(); + // selectionIndicator.style.left = `${rect.left - toggleContainer.getBoundingClientRect().left}px`; + // } + // } // helper function to set up mouse events function createMouseEvents() { @@ -219,6 +240,13 @@ return {handleMouseOver, handleMouseMove, handleMouseOut} } + function handleCheckboxChange(index) { + console.log(checkboxStates); + console.log(checkboxStates.value[index]); + // Call the corresponding function based on the index + checkboxFunctions[index](checkboxStates.value[index]); + } + // call helper function const { handleMouseOver, handleMouseMove, handleMouseOut } = createMouseEvents(); @@ -244,11 +272,11 @@ .attr('width', width) .attr('height', height) - const { radiusScale } = createScales(data) + const radius = 10 const categories = Array.from(new Set(data.value.map((d) => d.dimension))) - console.log(categories) - const categoryCenters = {} + // console.log(categories) + // const categoryCenters = {} const numRows = 2 //Math.ceil(Math.sqrt(categories.length)); // Number of rows for grid layout const numCols = 4 //Math.ceil(categories.length / numRows); // Number of columns @@ -281,7 +309,7 @@ // define forces that control the bubbles const forceX = d3.forceX((d) => categoryCenters[d.dimension].x).strength(0.2) const forceY = d3.forceY((d) => categoryCenters[d.dimension].y).strength(0.1) - const forceCollide = d3.forceCollide((d) => radiusScale(d.evidence_val) + 2) + const forceCollide = d3.forceCollide(radius + 2) const forceManyBody = d3.forceManyBody().strength(-15) // Adjust the strength as needed // add bubbles to the page @@ -291,7 +319,7 @@ .enter() .append('circle') .attr('class', 'bubble') - .attr('r', (d) => radiusScale(d.evidence_val)) + .attr('r', radius) .style('fill', (d) => dimensionColors[d.dimension.replace(' ', '')]) const simulation = d3 @@ -315,6 +343,132 @@ } } + function sizeByEvidence(checked) { + if (checked){ + const { radiusScale } = createScales(data) + + const forceCollide = d3.forceCollide((d) => radiusScale(d.evidence_val) + 2) + const forceManyBody = d3.forceManyBody().strength(15) // Adjust the strength as needed + + const bubbles = svg.selectAll('.bubble') + .attr('r', d => radiusScale(d.evidence_val)) // size bubbles based on evidence_val + + const simulation = d3 + .forceSimulation() + // .force('x', forceX) + // .force('y', forceY) + .force('collide', forceCollide) + .force('charge', forceManyBody) + .nodes(data.value) + .on('tick', ticked) + .alpha(0.2) + .restart() + + function ticked() { + bubbles.attr('cx', (d) => d.x).attr('cy', (d) => d.y) + } + } else { + const radius = 10 + + const forceX = d3.forceX((d) => categoryCenters[d.dimension].x).strength(0.2) + const forceY = d3.forceY((d) => categoryCenters[d.dimension].y).strength(0.1) + const forceCollide = d3.forceCollide(radius + 2) + const forceManyBody = d3.forceManyBody().strength(-15) // Adjust the strength as needed + + const bubbles = svg.selectAll('.bubble') + .attr('r', radius) + + const simulation = d3 + .forceSimulation() + .force('x', forceX) + .force('y', forceY) + .force('collide', forceCollide) + .force('charge', forceManyBody) + .nodes(data.value) + .on('tick', ticked) + .alpha(0.2) + .restart() + + function ticked() { + bubbles.attr('cx', (d) => d.x).attr('cy', (d) => d.y) + } + } + } + + function positionByAgreement(checked) { + if (checked) { + const { xScale, radiusScale} = createScales(data); + + const xAxis = svg.append('g') + .attr("transform", "translate(0," + (height - 50) + ")") + .call(d3.axisBottom(xScale)) + + // add label to x axis + svg.append('text') + .attr("class", "xLabel") + .attr("text-anchor", "middle") + .attr("x", width/2) + .attr("y", height-10) + .text("Level of Agreement"); + + const forceX = d3.forceX(d => xScale(d.level_agreement)).strength(1); + const forceY = d3.forceY((height/1.5) - (margin.bottom/2))//.strength(0.1); + const forceCollide = d3.forceCollide(d => radiusScale(d.evidence_val) + 2); //forceCollide for bubbles sized by evidence_val + const forceManyBody = d3.forceManyBody().strength(-15); // Adjust the strength as needed + + const bubbles = svg.selectAll(".bubble") + + simulation = d3.forceSimulation() + .force("x", forceX) + .force("y", forceY) + .force("collide", forceCollide) + .force("charge", forceManyBody) + .nodes(data.value) + .on("tick", ticked) + .alpha(.2) + .restart(); + + function ticked() { + bubbles + .attr("cx", d => d.x) + .attr("cy", d => d.y); + } + } else { + + } + } + + function sortByDirection() { + const { xScale, radiusScale} = createScales(data); + + var yScale = d3.scaleBand() + .domain(data.value.map(function(d) { return d.sig_name; })) + .range([margin.top, height]) + + const bubbles = svg.selectAll(".bubble") + + // const forceX = d3.forceX(d => xScale(d.level_agreement)).strength(.4); + const forceY = d3.forceY(d => yScale(d.sig_name)).strength(.4) + const forceCollide = d3.forceCollide(d => radiusScale(d.evidence_val) + 1); //forceCollide for bubbles sized by evidence_val + const forceManyBody = d3.forceManyBody().strength(-15); // Adjust the strength as needed + + simulation = d3.forceSimulation() + // .force("x", forceX) + .force("y", forceY) + .force("collide", forceCollide) + .force("charge", forceManyBody) + .nodes(data.value) + .on("tick", ticked) + .alpha(.2) + .restart(); + + function ticked() { + bubbles + .attr("cx", d => d.x) + .attr("cy", d => d.y); + } + } + // initiate chart with determinant indicator dataset function createBeeswarmChart() { diff --git a/src/components/Dendrogram.vue b/src/components/Dendrogram.vue index 6932b5dcc0a5836d2ccbe5cbb66e0676a4ceecd2..9aa4b1a18ce1b617e0963c8e95d06afad2108bf0 100644 --- a/src/components/Dendrogram.vue +++ b/src/components/Dendrogram.vue @@ -46,6 +46,17 @@ const data = ref([]); const mobileView = isMobile; const metaText = metaAnalysisText.metaAnalysisText; +// set colors for lines +const dimensionColors = { + Demographiccharacteristics: "#625D0B", + Landtenure: "#5C0601", + Livingconditions: "#0B4E8B", + Socioeconomicstatus: "#DC8260", + Health: "#7F4A89", + Riskperception: "#249CB1", + Exposure: "#B47D83" +} + // load data and then make chart onMounted(() => { loadData().then(() => { @@ -63,7 +74,7 @@ onMounted(() => { async function loadData() { // console.log(data); - data.value = await d3.csv(publicPath + 'p2_agree_evid_stats.csv', (d) => { + data.value = await d3.csv(publicPath + 'indicator_uncertainty.csv', (d) => { d.level_agreement = +d.level_agreement d.evidence_val = +d.evidence_val return d @@ -290,11 +301,11 @@ function createDendrogram(result) { } // Define a color scale for the nodes based on category names and depth - const colorScale = d3.scaleOrdinal() - .domain(["Demographic Characteristics", "Exposure", "Health", "Land Tenure", "Living Conditions", "Risk Perception", "Socioeconomic Status"]) - .range(["#625D0B", "#5C0601", "#0B4E8B", "#DC8260", "#7F4A89", "#249cb1", "#B47D83"]); - - // Function to determine the color of a node based on its category + const colorScale = d3.scaleOrdinal() + .domain(["Demographic characteristics", "Exposure", "Health", "Land tenure", "Living conditions", "Risk perception", "Socioeconomic status"]) + .range(Object.entries(dimensionColors).map(([key, value]) => value)); + + // Function to determine the color of a node based on its category function getNodeColor(d) { // If the node is one of the main categories, return its color if (d.depth === 1) {