From 0d3c6d21e7a39dda2f6e149b6b6011e426fe358b Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Wed, 16 Oct 2024 16:06:11 -0500
Subject: [PATCH 01/38] add viz page for wildfire aerosols viz

---
 src/assets/content/ChartGrid.js        | 12 +++++-
 src/assets/text/authors.js             | 24 +++++++++++
 src/assets/text/text.js                |  8 ++++
 src/components/WildfireAerosolsViz.vue | 57 ++++++++++++++++++++++++++
 4 files changed, 100 insertions(+), 1 deletion(-)
 create mode 100644 src/components/WildfireAerosolsViz.vue

diff --git a/src/assets/content/ChartGrid.js b/src/assets/content/ChartGrid.js
index fc943d8..59e6fc6 100644
--- a/src/assets/content/ChartGrid.js
+++ b/src/assets/content/ChartGrid.js
@@ -31,6 +31,16 @@ export default {
             alt: '',
             chartOrder: 1,
             description: 'Ice cores can record changes in wildfire prevalence.'
+        },
+        {
+            title: 'Wildfire Aerosols',
+            project: 'Fire in Ice',
+            vizKey: 'WildfireAerosols',
+            vizRoute: 'wildfire-aerosols',
+            img_src: 'placeholder_thumbnail.png',
+            alt: '',
+            chartOrder: 2,
+            description: 'Wildfires are depositing aerosols on glaciers.'
         },          
         {
             title: 'Wildfire Aerosols',
@@ -39,7 +49,7 @@ export default {
             vizRoute: 'aerosol-paths',
             img_src: 'aerosols_thumbnail.png',
             alt: '',
-            chartOrder: 2,
+            chartOrder: 3,
             description: 'Wildfire particles are deposited on glaciers.'
         },
         {
diff --git a/src/assets/text/authors.js b/src/assets/text/authors.js
index 1e52356..1a71294 100644
--- a/src/assets/text/authors.js
+++ b/src/assets/text/authors.js
@@ -88,6 +88,30 @@ export default {
     additionalAuthors: [
     ]
   },
+  WildfireAerosols: {
+    primaryAuthors: [
+      {
+        firstName: 'Hayley',
+        lastName: 'Corson-Dosch',
+        fullName: 'Hayley Corson-Dosch',
+        initials: 'HCD',
+        profile_link: 'https://www.usgs.gov/staff-profiles/hayley-corson-dosch',
+        role: 'lead author',
+        contribution: 'led the creation of the interactive chart'
+      },
+      {
+        firstName: 'Jeffrey',
+        lastName: 'Kwang',
+        fullName: 'Jeffrey Kwang',
+        initials: 'JK',
+        profile_link: 'https://www.usgs.gov/staff-profiles/jeffrey-kwang',
+        role: 'contributor',
+        contribution: 'led the development of the data visualization'
+      },
+    ],
+    additionalAuthors: [
+    ]
+  },
   Aerosols: {
     primaryAuthors: [
       {
diff --git a/src/assets/text/text.js b/src/assets/text/text.js
index c36fc6e..d813866 100644
--- a/src/assets/text/text.js
+++ b/src/assets/text/text.js
@@ -1,3 +1,5 @@
+import WildfireAerosolsViz from "../../components/WildfireAerosolsViz.vue";
+
 export default {
     landingPage: {
         pageTitle: "Earth is changing...",
@@ -175,6 +177,12 @@ export default {
         },
         FishAsFoodSankey: {
             paragraph1: 'Explore total recreational harvest for the five families of inland fish with the largest recreational harvests: <span class="scientificName">Cyprinidae</span> (minnows and carps), <span class="scientificName">Percidae</span> (perch), <span class="scientificName">Salmonidae</span> (salmon, trout, grayling, and whitefish), <span class="scientificName">Bagridae</span> (bagrid catfish), and <span class="scientificName">Centrarchidae</span> (sunfishes). Total recreational harvest is broken out by family, by species, and by country.  Hover over the chart to see the harvest totals, in kilograms'
+        },
+        WildfireAerosols: {
+            paragraph1: "In the cores, we see that there is more than just ice. Particles from the air, called aerosols, have been deposited in the ice.",
+            paragraph2: "These aerosols can come from dust, fossil fuel combustion, or wildfires.",
+            paragraph3: "What are the particles made of? These L, M, and G sugars indicate that some of the particules have been deposited by wildfires.",   
+            paragraph4: "Some L/(M+G) peaks indicate that hardwoods were burned, which suggests a further source. Based on hardwood forest locations, one likely source is East Asia."
         }
     }
 }
\ No newline at end of file
diff --git a/src/components/WildfireAerosolsViz.vue b/src/components/WildfireAerosolsViz.vue
new file mode 100644
index 0000000..5185269
--- /dev/null
+++ b/src/components/WildfireAerosolsViz.vue
@@ -0,0 +1,57 @@
+<template>
+    <!---VizSection-->
+    <VizSection
+        id="cross-section"
+        :figures="true"
+        :fig-caption="false"
+    >
+        <!-- HEADING -->
+        <template #heading>
+            <h2>
+                {{ text.heading }}
+            </h2>
+        </template>
+        <!-- FIGURES -->
+        <template #aboveExplanation>
+            <p v-html="text.paragraph1" />
+            <p v-html="text.paragraph2" />
+            <p v-html="text.paragraph3" />
+            <p v-html="text.paragraph4" />
+        </template>
+        <template #figures>
+        </template>
+        <!-- FIGURE CAPTION -->
+        <template #figureCaption>
+        </template>
+        <!-- EXPLANATION -->
+        <template #belowExplanation>
+        </template>
+    </VizSection>
+</template>
+
+<script setup>
+    import { onMounted } from "vue";
+    import * as d3 from 'd3';
+    import VizSection from '@/components/VizSection.vue';
+
+    // define props
+    defineProps({
+        text: { type: Object }
+    })
+
+    // Declare behavior on mounted
+    // functions called here
+    onMounted(async () => {
+        try {
+            console.log('app')
+        } catch (error) {
+            console.error('Error during component mounting', error);
+        }        
+    });
+</script>
+
+<style scoped lang="scss">
+</style>
+<style lang="scss">
+/* css for elements added/classed w/ d3 */
+</style>
\ No newline at end of file
-- 
GitLab


From 2d9731096af56b787d04a410c394f8dfc11d6275 Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Wed, 16 Oct 2024 20:55:56 -0500
Subject: [PATCH 02/38] remove typo

---
 src/assets/text/text.js | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/assets/text/text.js b/src/assets/text/text.js
index d813866..486cea3 100644
--- a/src/assets/text/text.js
+++ b/src/assets/text/text.js
@@ -1,5 +1,3 @@
-import WildfireAerosolsViz from "../../components/WildfireAerosolsViz.vue";
-
 export default {
     landingPage: {
         pageTitle: "Earth is changing...",
-- 
GitLab


From cb81c218f7b3704385eb9f82a5e91bef2849ef25 Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Wed, 16 Oct 2024 20:56:11 -0500
Subject: [PATCH 03/38] core as bar chart

---
 src/components/WildfireAerosolsViz.vue | 347 ++++++++++++++++++++++++-
 1 file changed, 338 insertions(+), 9 deletions(-)

diff --git a/src/components/WildfireAerosolsViz.vue b/src/components/WildfireAerosolsViz.vue
index 5185269..b28d96c 100644
--- a/src/components/WildfireAerosolsViz.vue
+++ b/src/components/WildfireAerosolsViz.vue
@@ -5,13 +5,11 @@
         :figures="true"
         :fig-caption="false"
     >
-        <!-- HEADING -->
         <template #heading>
             <h2>
                 {{ text.heading }}
             </h2>
         </template>
-        <!-- FIGURES -->
         <template #aboveExplanation>
             <p v-html="text.paragraph1" />
             <p v-html="text.paragraph2" />
@@ -19,18 +17,17 @@
             <p v-html="text.paragraph4" />
         </template>
         <template #figures>
+            <div id="chart-container" class="maxWidth" ref="chart"></div>
         </template>
-        <!-- FIGURE CAPTION -->
         <template #figureCaption>
         </template>
-        <!-- EXPLANATION -->
         <template #belowExplanation>
         </template>
     </VizSection>
 </template>
 
 <script setup>
-    import { onMounted } from "vue";
+    import { onMounted, ref } from "vue";
     import * as d3 from 'd3';
     import VizSection from '@/components/VizSection.vue';
 
@@ -39,19 +36,351 @@
         text: { type: Object }
     })
 
-    // Declare behavior on mounted
-    // functions called here
+    // global variables
+    const publicPath = import.meta.env.BASE_URL;
+    const dataFile = 'fii_core4data.csv'
+    const data = ref();
+    const chart = ref(null);
+    const chartTitle = 'Title of chart';
+    let chartDimensions;
+    let chartBounds;
+    let xScale;
+    let xAxis;
+    let yScale;
+    let yAxis;
+    const colors = {warm: '#FF7256', cool: '#5CACEE', cold: '#36648B'}
+    let colorScale;
+
+    // Behavior on mounted (functions called here)
+    // Load data and then make chart
     onMounted(async () => {
         try {
-            console.log('app')
+            await loadDatasets();
+
+            if (data.value.length > 0) {
+                // initialize chart elements
+                initChart({
+                    width: chart.value.offsetWidth,
+                    height: window.innerHeight * 0.8,
+                    margin: 10,
+                    marginBottom: 50,
+                    marginLeft: 60});
+
+                // draw chart
+                drawChart(data.value);
+            } else {
+                console.error('Error loading data');
+            }
         } catch (error) {
             console.error('Error during component mounting', error);
-        }        
+        }
     });
+
+    async function loadDatasets() {
+        try {
+            data.value = await loadData(dataFile);
+            console.log('data in');
+        } catch (error) {
+            console.error('Error loading datasets', error);
+        }
+    }
+
+    async function loadData(fileName) {
+        try {
+            const data = await d3.csv(publicPath + fileName, d => {
+                d.total = +d.total;
+                return d;
+            });
+            return data;
+        } catch (error) {
+            console.error(`Error loading data from ${fileName}`, error);
+            return [];
+        }
+    }
+
+    function initChart({
+        width = 500, // outer width, in pixels
+        height = 500, // outer height, in pixels
+        margin = 1, // default margins
+        marginTop = margin, // top margin, in pixels
+        marginBottom = margin, // left margin, in pixels
+        marginLeft = margin, // left margin, in pixels
+        marginRight = margin // right margin, in pixels
+    }) {
+        // set up global chart dimensions, including bounded dimensions
+        chartDimensions = {
+            width,
+            height,
+            margin: {
+                top: marginTop,
+                right: marginRight,
+                bottom: marginBottom,
+                left: marginLeft
+            },
+            boundedWidth: width - marginLeft - marginRight,
+            boundedHeight: height - marginTop - marginBottom
+        }
+
+        // draw canvas for chart
+        const chartSVG = d3.select("#chart-container")
+            .append("svg")
+                .attr("viewBox", [0, 0, (chartDimensions.width), (chartDimensions.height)].join(' '))
+                .attr("width", "100%")
+                .attr("height", "100%")
+                .attr("id", "wrapper");
+
+        // assign role for accessibility
+        chartSVG.attr("role", "figure")
+            .append("title")
+            .text(chartTitle);
+
+        // Add group for bounds
+        chartBounds = chartSVG.append("g")
+            .attr("id", "chart-bounds")
+            .style("transform", `translate(${
+                chartDimensions.margin.left
+            }px, ${
+                chartDimensions.margin.top
+            }px)`);
+
+        // Initialize scales
+        // initXScale()
+        initYScale()
+        initColorScale()
+
+        // Initialize axes
+        // initXAxis()
+        initYAxis()
+
+        // Add groups for visual elements
+        chartBounds.append("g")
+            .attr("class", "rects");
+    }
+
+    // function initXScale() {
+    //     // scale for x axis (domain set in `drawChart()`)
+    //     xScale = d3.scaleLinear()
+    //         .range([0, chartDimensions.boundedWidth]);
+    // }
+
+    // function initXAxis() {
+    //     // add group for x axis
+    //     xAxis = chartBounds.append("g")
+    //         .attr("id", "x-axis")
+    //         .attr("class", "axis")
+    //         .attr("transform", `translate(0,${chartDimensions.boundedHeight})`)
+    //         .attr("aria-hidden", true); // hide from screen reader
+    // }
+
+    function initYScale() {
+        // scale for the y axis (domain set in `drawChart()`)
+        yScale = d3.scaleBand()
+            .range([0, chartDimensions.boundedHeight])
+            .padding(0.1);
+    }
+
+    function initYAxis() {
+        // add group for y axis
+        yAxis = chartBounds.append("g")
+            .attr("id", "y-axis")
+            .attr("class", "axis")
+            .attr("aria-hidden", true);
+    }
+
+    function drawAxis({
+        axis,
+        axisScale,
+        axisFxn
+    }, {
+        axisTitle = '',
+        titleX = -chartDimensions.boundedHeight / 2,
+        titleY = -chartDimensions.margin.left,
+        titleTextAnchor = "middle",
+        titleBaseline = "text-before-edge",
+        titleAngle = -90,
+        tickSize = 0,
+        tickPadding = 5,
+        tickType = "numeric",
+        tickFormat = ".5f",
+        textAngle = 0,
+        keepDomain = true,
+    }) {
+        // generate axis
+        // if numeric ticks, include specification of format
+        if (tickType == "numeric") {
+            axis
+                .call(d3[axisFxn](axisScale).tickSize(tickSize).tickPadding(tickPadding).tickFormat(d3.format(tickFormat)));
+        } else {
+            axis
+                .call(d3[axisFxn](axisScale).tickSize(tickSize).tickPadding(tickPadding));
+        }
+
+        // if specified, remove axis line
+        if (!keepDomain) {
+            axis.select(".domain").remove();
+        }
+
+        // add class to axis labels and rotate as specified
+        axis.selectAll('text')
+            .attr("class", "axis-text")
+            .attr("transform", `rotate(${textAngle})`);
+
+        // add axis title
+        axis
+            .append("text")
+            .attr("class", "axis-title")
+            .attr("x", titleX)
+            .attr("y", titleY)
+            .attr("transform", `rotate(${titleAngle})`)
+            .attr("text-anchor", titleTextAnchor)
+            .attr("dominant-baseline", titleBaseline)
+            .attr("role", "presentation")
+            .attr("aria-hidden", true)
+            .text(axisTitle);
+    }
+
+    // function drawXAxis({
+    //     axisTitle = '',
+    //     titleX = chartDimensions.boundedWidth / 2,
+    //     titleY = chartDimensions.margin.bottom,
+    //     titleTextAnchor = "middle",
+    //     titleBaseline = "text-after-edge",
+    //     titleAngle = 0,
+    //     tickSize = 0,
+    //     tickPadding = 5,
+    //     tickType = 'numeric',
+    //     tickFormat = ".2f",
+    //     textAngle = 0, 
+    //     keepDomain = true,
+    // }) {
+    //     drawAxis({
+    //         axis: xAxis,
+    //         axisScale: xScale,
+    //         axisFxn: 'axisBottom'
+    //     }, {
+    //         axisTitle: axisTitle,
+    //         titleX: titleX,
+    //         titleY: titleY,
+    //         titleTextAnchor: titleTextAnchor,
+    //         titleBaseline: titleBaseline,
+    //         titleAngle: titleAngle,tickSize: tickSize,
+    //         tickPadding: tickPadding,
+    //         tickType: tickType,
+    //         tickFormat: tickFormat,
+    //         textAngle: textAngle,
+    //         keepDomain: keepDomain,
+    //     })
+    // }
+
+    function drawYAxis({
+        axisTitle = '',
+        titleX = -chartDimensions.boundedHeight / 2,
+        titleY = -chartDimensions.margin.left,
+        titleTextAnchor = "middle",
+        titleBaseline = "text-before-edge",
+        titleAngle = -90,
+        tickSize = 0,
+        tickPadding = 5,
+        tickType = 'numeric',
+        tickFormat = ".2f",
+        textAngle = 0,
+        keepDomain = true,
+    }) {
+        drawAxis({
+            axis: yAxis,
+            axisScale: yScale,
+            axisFxn: 'axisLeft'
+        }, {
+            axisTitle: axisTitle,
+            titleX: titleX,
+            titleY: titleY,
+            titleTextAnchor: titleTextAnchor,
+            titleBaseline: titleBaseline,
+            titleAngle: titleAngle,
+            tickSize: tickSize,
+            tickPadding: tickPadding,
+            tickType: tickType,
+            textAngle: textAngle,
+            tickFormat: tickFormat,
+            keepDomain: keepDomain,
+        })
+    }
+
+    function initColorScale() {
+        colorScale = d3.scaleSequential()           
+            .interpolator(d3.interpolateGreys);
+    }
+
+    function drawChart(data) {
+        //////////////////////////////
+        /////    PROCESS DATA    /////
+        //////////////////////////////
+
+        ///////////////////////////////////////////
+        /////    SET UP ACCESSOR FUNCTIONS    /////
+        ///////////////////////////////////////////
+        const yAccessor = d => d.depth_cm;
+        // const xAccessor = d => d.total;
+        const colorAccessor = d => d.total;
+        const identifierAccessor = d => 'depth-' + d.depth_cm;
+
+        ///////////////////////////////////////////
+        /////    FINISH SETTING UP Y SCALE    /////
+        ///////////////////////////////////////////
+        // set domain for yScale, based on data
+        yScale
+            .domain([... new Set(data.map(d => yAccessor(d)))]);
+        drawYAxis({axisTitle: 'Depth (cm)', tickType: 'string'})
+        
+        ///////////////////////////////////////////
+        /////    FINISH SETTING UP X SCALE    /////
+        ///////////////////////////////////////////
+        // // set domain for xScale
+        // xScale
+        //     .domain([0, d3.max(data, xAccessor)]);
+        // drawXAxis({axisTitle: 'Climate vulnerability'})
+
+        ///////////////////////////////////
+        /////    SET UP COLOR SCALE   /////
+        ///////////////////////////////////
+        colorScale
+            .domain(d3.extent(data, colorAccessor));
+
+        ////////////////////////////////////
+        /////    ADD CHART ELEMENTS    /////
+        ////////////////////////////////////
+        // draw chart
+        chartBounds.select('.rects') // selects our group we set up to hold chart elements
+            .selectAll(".rect") // empty selection
+                .data(data) // bind data
+                .enter() // instantiate chart element for each element of data
+                .append("rect") // append a rectangle for each element
+                    .attr("class", "rect")
+                    .attr("id", d => 'bar-' + identifierAccessor(d))
+                    .attr("x", 0) //d => xScale(xAccessor(d)))
+                    .attr("y", d => yScale(yAccessor(d)))
+                    .attr("height", yScale.bandwidth())
+                    .attr("width", 50)
+                    .style("fill", d => colorScale(colorAccessor(d)));
+
+    }
 </script>
 
 <style scoped lang="scss">
 </style>
 <style lang="scss">
 /* css for elements added/classed w/ d3 */
+    .axis-text {
+        font-size: 1.6rem;
+        font-family: var(--default-font);
+        user-select: none;
+    }
+    .axis-title {
+        font-size: 1.8rem;
+        font-family: var(--default-font);
+        font-weight: 900;
+        fill: var(--color-text);
+        user-select: none;
+    }
+
 </style>
\ No newline at end of file
-- 
GitLab


From d68c90541b533edd68f75ce39c7d2aa80277966c Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Wed, 16 Oct 2024 21:30:27 -0500
Subject: [PATCH 04/38] linear y axis

---
 src/components/WildfireAerosolsViz.vue | 70 ++++++++++++++++++++------
 1 file changed, 56 insertions(+), 14 deletions(-)

diff --git a/src/components/WildfireAerosolsViz.vue b/src/components/WildfireAerosolsViz.vue
index b28d96c..594bd9c 100644
--- a/src/components/WildfireAerosolsViz.vue
+++ b/src/components/WildfireAerosolsViz.vue
@@ -44,11 +44,10 @@
     const chartTitle = 'Title of chart';
     let chartDimensions;
     let chartBounds;
-    let xScale;
-    let xAxis;
+    // let xScale;
+    // let xAxis;
     let yScale;
     let yAxis;
-    const colors = {warm: '#FF7256', cool: '#5CACEE', cold: '#36648B'}
     let colorScale;
 
     // Behavior on mounted (functions called here)
@@ -64,7 +63,7 @@
                     height: window.innerHeight * 0.8,
                     margin: 10,
                     marginBottom: 50,
-                    marginLeft: 60});
+                    marginLeft: 100});
 
                 // draw chart
                 drawChart(data.value);
@@ -88,6 +87,7 @@
     async function loadData(fileName) {
         try {
             const data = await d3.csv(publicPath + fileName, d => {
+                d.depth_cm = +d.depth_cm;
                 d.total = +d.total;
                 return d;
             });
@@ -155,6 +155,8 @@
         // Add groups for visual elements
         chartBounds.append("g")
             .attr("class", "rects");
+        chartBounds.append("g")
+            .attr("class", "annotations");
     }
 
     // function initXScale() {
@@ -174,9 +176,8 @@
 
     function initYScale() {
         // scale for the y axis (domain set in `drawChart()`)
-        yScale = d3.scaleBand()
-            .range([0, chartDimensions.boundedHeight])
-            .padding(0.1);
+        yScale = d3.scaleLinear()
+            .range([0, chartDimensions.boundedHeight]);
     }
 
     function initYAxis() {
@@ -202,14 +203,18 @@
         tickPadding = 5,
         tickType = "numeric",
         tickFormat = ".5f",
+        customSuffix = null,
         textAngle = 0,
         keepDomain = true,
     }) {
         // generate axis
         // if numeric ticks, include specification of format
-        if (tickType == "numeric") {
+        if (tickType == "numeric" && !customSuffix) {
             axis
                 .call(d3[axisFxn](axisScale).tickSize(tickSize).tickPadding(tickPadding).tickFormat(d3.format(tickFormat)));
+        } else if (tickType == "numeric" && customSuffix) {
+            axis
+                .call(d3[axisFxn](axisScale).tickSize(tickSize).tickPadding(tickPadding).tickFormat(d => d3.format(tickFormat)(d) + ' ' + customSuffix));
         } else {
             axis
                 .call(d3[axisFxn](axisScale).tickSize(tickSize).tickPadding(tickPadding));
@@ -250,6 +255,7 @@
     //     tickPadding = 5,
     //     tickType = 'numeric',
     //     tickFormat = ".2f",
+    //     customSuffix = null,
     //     textAngle = 0, 
     //     keepDomain = true,
     // }) {
@@ -267,6 +273,7 @@
     //         tickPadding: tickPadding,
     //         tickType: tickType,
     //         tickFormat: tickFormat,
+    //         customSuffix: customSuffix,
     //         textAngle: textAngle,
     //         keepDomain: keepDomain,
     //     })
@@ -283,6 +290,7 @@
         tickPadding = 5,
         tickType = 'numeric',
         tickFormat = ".2f",
+        customSuffix = null,
         textAngle = 0,
         keepDomain = true,
     }) {
@@ -302,6 +310,7 @@
             tickType: tickType,
             textAngle: textAngle,
             tickFormat: tickFormat,
+            customSuffix: customSuffix,
             keepDomain: keepDomain,
         })
     }
@@ -329,8 +338,8 @@
         ///////////////////////////////////////////
         // set domain for yScale, based on data
         yScale
-            .domain([... new Set(data.map(d => yAccessor(d)))]);
-        drawYAxis({axisTitle: 'Depth (cm)', tickType: 'string'})
+            .domain([0, d3.max(data, yAccessor) + 10]);
+        drawYAxis({tickFormat: ".0f", customSuffix: 'cm', tickSize: 3, keepDomain: false})
         
         ///////////////////////////////////////////
         /////    FINISH SETTING UP X SCALE    /////
@@ -357,12 +366,43 @@
                 .append("rect") // append a rectangle for each element
                     .attr("class", "rect")
                     .attr("id", d => 'bar-' + identifierAccessor(d))
-                    .attr("x", 0) //d => xScale(xAccessor(d)))
+                    .attr("x", 40)
                     .attr("y", d => yScale(yAccessor(d)))
-                    .attr("height", yScale.bandwidth())
+                    .attr("height", chartDimensions.boundedHeight / data.length)
                     .attr("width", 50)
                     .style("fill", d => colorScale(colorAccessor(d)));
-
+        // draw year bands
+        chartBounds.select(".annotations")
+            .append("rect")
+                .attr("class", "year-bands")
+                .attr("x", 25)
+                .attr("y", 0)
+                .attr("height", yScale(372))
+                .attr("width", 3)
+
+        chartBounds.select(".annotations")
+            .append("rect")
+                .attr("class", "year-bands")
+                .attr("x", 25)
+                .attr("y", yScale(378))
+                .attr("height", chartDimensions.boundedHeight - yScale(378))
+                .attr("width", 3)
+
+        chartBounds.select(".annotations")
+            .append("text")
+                .attr("x", - yScale(372) / 2)
+                .attr("y", 20)
+                .attr("transform", "rotate(-90)")
+                .attr("text-anchor", "middle")
+                .text("2016 accumulation")
+        
+        chartBounds.select(".annotations")
+            .append("text")
+                .attr("x", - yScale(378) - ((chartDimensions.boundedHeight - yScale(378)) / 2))
+                .attr("y", 20)
+                .attr("transform", "rotate(-90)")
+                .attr("text-anchor", "middle")
+                .text("2015 accumulation")
     }
 </script>
 
@@ -382,5 +422,7 @@
         fill: var(--color-text);
         user-select: none;
     }
-
+    .year-bands {
+        fill: var(--medium-grey);
+    }
 </style>
\ No newline at end of file
-- 
GitLab


From fcf27a4440f4aa0d96cef64d0692eb21886c07ba Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Thu, 17 Oct 2024 13:09:01 -0500
Subject: [PATCH 05/38] add stacked bar and scatter plots

---
 public/fii_core4biomass.csv            |  36 ++
 public/fii_core4particulates.csv       |  79 +++++
 public/fii_core4sugars.csv             | 235 ++++++++++++
 src/components/WildfireAerosolsViz.vue | 472 +++++++++++++++++++++----
 4 files changed, 746 insertions(+), 76 deletions(-)
 create mode 100644 public/fii_core4biomass.csv
 create mode 100644 public/fii_core4particulates.csv
 create mode 100644 public/fii_core4sugars.csv

diff --git a/public/fii_core4biomass.csv b/public/fii_core4biomass.csv
new file mode 100644
index 0000000..460d5a9
--- /dev/null
+++ b/public/fii_core4biomass.csv
@@ -0,0 +1,36 @@
+depth_cm,year,site,vegetation_type,burned
+420,2016,4,grass,TRUE
+600,2016,4,grass,TRUE
+710,2016,4,grass,TRUE
+0,2016,4,softwood,TRUE
+10,2016,4,softwood,TRUE
+20,2016,4,softwood,TRUE
+30,2016,4,softwood,TRUE
+40,2016,4,softwood,TRUE
+190,2016,4,softwood,TRUE
+240,2016,4,softwood,TRUE
+350,2016,4,softwood,TRUE
+420,2016,4,softwood,TRUE
+430,2016,4,softwood,TRUE
+450,2016,4,softwood,TRUE
+460,2016,4,softwood,TRUE
+470,2016,4,softwood,TRUE
+480,2016,4,softwood,TRUE
+490,2016,4,softwood,TRUE
+500,2016,4,softwood,TRUE
+540,2016,4,softwood,TRUE
+560,2016,4,softwood,TRUE
+600,2016,4,softwood,TRUE
+610,2016,4,softwood,TRUE
+630,2016,4,softwood,TRUE
+710,2016,4,softwood,TRUE
+760,2016,4,softwood,TRUE
+20,2016,4,hardwood,TRUE
+410,2016,4,hardwood,TRUE
+420,2016,4,hardwood,TRUE
+440,2016,4,hardwood,TRUE
+600,2016,4,hardwood,TRUE
+610,2016,4,hardwood,TRUE
+630,2016,4,hardwood,TRUE
+700,2016,4,hardwood,TRUE
+710,2016,4,hardwood,TRUE
diff --git a/public/fii_core4particulates.csv b/public/fii_core4particulates.csv
new file mode 100644
index 0000000..33e7258
--- /dev/null
+++ b/public/fii_core4particulates.csv
@@ -0,0 +1,79 @@
+depth_cm,total
+0,10649.124603255857
+10,6517.687661777392
+20,7194.116029694805
+30,2553.2219399620735
+40,3828.6557332052425
+50,5324.314949705166
+60,5122.428991185112
+70,8585.034478050115
+80,3465.0477509141892
+90,8925.250230709167
+100,5292.609403693565
+110,4272.285813388829
+120,3507.3874500260713
+130,2601.9491291125246
+140,23245.73248848723
+150,8556.396721766912
+160,4964.509824092172
+170,7371.250216750478
+180,3024.5740158296753
+190,5052.671440374553
+200,2163.817565229428
+210,1937.3945383794207
+220,7072.214317937328
+230,6577.5957801221575
+240,3785.5344980752134
+250,4787.178866567548
+260,4456.157497905615
+270,8133.7913578014795
+280,4471.107098208085
+290,4852.294372294374
+300,3843.4064010084735
+310,3942.5215457325535
+320,1811.7573700072753
+330,2968.978229115948
+340,4082.8896544524464
+350,5722.032259178668
+360,9876.112371313904
+370,3780.699923467614
+380,6947.298525197457
+390,18667.89976966567
+400,15618.800913459901
+410,6543.201516793067
+420,10078.145872294954
+430,5300.020479213597
+440,3628.75565921251
+450,4149.009120208463
+460,8060.309419496167
+470,4447.573582443247
+480,6968.490423560103
+490,8326.992287917737
+500,4542.294720903023
+510,4225.168739507313
+520,3856.9367140795707
+530,5006.950801885191
+540,6809.39083669459
+550,6203.350187459157
+560,7766.210486400772
+570,4901.893287435458
+580,2594.117848930986
+590,9764.06346589739
+600,4485.608138606301
+610,4512.054708488886
+620,3558.4987419625395
+630,3927.8487195589964
+640,4536.4366083634195
+650,6263.971880492089
+660,6295.0257289879955
+670,5090.137485970816
+680,5551.951486091035
+690,8621.715245459956
+700,4135.907226700608
+710,5105.34903518729
+720,10222.35598201324
+730,6059.069318952894
+740,9482.5702301646
+750,5506.235986547085
+760,3414.458342166632
+770,4628.862359550563
diff --git a/public/fii_core4sugars.csv b/public/fii_core4sugars.csv
new file mode 100644
index 0000000..d216105
--- /dev/null
+++ b/public/fii_core4sugars.csv
@@ -0,0 +1,235 @@
+depth_cm,year,site,sugar,picogram_per_mL,bar_order
+0,2016,4,Galactosan,3643.984956,2
+0,2016,4,Mannosan,185.978212,1
+0,2016,4,Levoglucosan,2042.967498,3
+10,2016,4,Galactosan,5102,2
+10,2016,4,Mannosan,247.422088,1
+10,2016,4,Levoglucosan,3448.288329,3
+20,2016,4,Galactosan,1737.561236,2
+20,2016,4,Mannosan,0,1
+20,2016,4,Levoglucosan,5642.333917,3
+30,2016,4,Galactosan,2991.032458,2
+30,2016,4,Mannosan,0,1
+30,2016,4,Levoglucosan,2367.054347,3
+40,2016,4,Galactosan,2871.645244,2
+40,2016,4,Mannosan,0,1
+40,2016,4,Levoglucosan,1582.720703,3
+50,2016,4,Galactosan,2730.745343,2
+50,2016,4,Mannosan,136.571233,1
+50,2016,4,Levoglucosan,773.704765,3
+60,2016,4,Galactosan,2627.17476,2
+60,2016,4,Mannosan,0,1
+60,2016,4,Levoglucosan,996.891497,3
+70,2016,4,Galactosan,2865.455356,2
+70,2016,4,Mannosan,0,1
+70,2016,4,Levoglucosan,658.519352,3
+80,2016,4,Galactosan,3408.246862,2
+80,2016,4,Mannosan,81.462962,1
+80,2016,4,Levoglucosan,155.133022,3
+90,2016,4,Galactosan,3116.492189,2
+90,2016,4,Mannosan,111.251638,1
+90,2016,4,Levoglucosan,10.693513,3
+100,2016,4,Galactosan,2991.0887,2
+100,2016,4,Mannosan,100.761708,1
+100,2016,4,Levoglucosan,0,3
+110,2016,4,Galactosan,3374.025934,2
+110,2016,4,Mannosan,107.261632,1
+110,2016,4,Levoglucosan,95.195389,3
+120,2016,4,Galactosan,3688.314687,2
+120,2016,4,Mannosan,211.881431,1
+120,2016,4,Levoglucosan,538.710328,3
+130,2016,4,Galactosan,2811.085927,2
+130,2016,4,Mannosan,95.351955,1
+130,2016,4,Levoglucosan,0,3
+140,2016,4,Galactosan,3571.721265,2
+140,2016,4,Mannosan,133.847472,1
+140,2016,4,Levoglucosan,261.518971,3
+150,2016,4,Galactosan,3421.617482,2
+150,2016,4,Mannosan,160.806579,1
+150,2016,4,Levoglucosan,521.30196,3
+160,2016,4,Galactosan,3137.909706,2
+160,2016,4,Mannosan,160.788247,1
+160,2016,4,Levoglucosan,321.265122,3
+170,2016,4,Galactosan,3477.895882,2
+170,2016,4,Mannosan,124.781049,1
+170,2016,4,Levoglucosan,234.10092,3
+180,2016,4,Galactosan,1012.444408,2
+180,2016,4,Mannosan,151.58182300000001,1
+180,2016,4,Levoglucosan,0,3
+190,2016,4,Galactosan,2659.173538,2
+190,2016,4,Mannosan,0,1
+190,2016,4,Levoglucosan,1691.89831,3
+200,2016,4,Galactosan,2231.051983,2
+200,2016,4,Mannosan,0,1
+200,2016,4,Levoglucosan,0,3
+210,2016,4,Galactosan,2481.934291,2
+210,2016,4,Mannosan,136.836857,1
+210,2016,4,Levoglucosan,343.812371,3
+220,2016,4,Galactosan,1467.559389,2
+220,2016,4,Mannosan,0,1
+220,2016,4,Levoglucosan,0,3
+230,2016,4,Galactosan,1427.884253,2
+230,2016,4,Mannosan,110.447276,1
+230,2016,4,Levoglucosan,0,3
+240,2016,4,Galactosan,2004.229731,2
+240,2016,4,Mannosan,126.648323,1
+240,2016,4,Levoglucosan,987.067253,3
+250,2016,4,Galactosan,2067.988772,2
+250,2016,4,Mannosan,83.723246,1
+250,2016,4,Levoglucosan,103.951064,3
+260,2016,4,Galactosan,2225.619045,2
+260,2016,4,Mannosan,242.512425,1
+260,2016,4,Levoglucosan,903.340161,3
+270,2016,4,Galactosan,4323.727311,2
+270,2016,4,Mannosan,148.66512,1
+270,2016,4,Levoglucosan,680.832755,3
+280,2016,4,Galactosan,3682.363141,2
+280,2016,4,Mannosan,119.495076,1
+280,2016,4,Levoglucosan,247.695565,3
+290,2016,4,Galactosan,2886.313206,2
+290,2016,4,Mannosan,258.930697,1
+290,2016,4,Levoglucosan,742.057646,3
+300,2016,4,Galactosan,3318.702077,2
+300,2016,4,Mannosan,95.642088,1
+300,2016,4,Levoglucosan,129.620377,3
+310,2016,4,Galactosan,2764.149683,2
+310,2016,4,Mannosan,202.026449,1
+310,2016,4,Levoglucosan,779.530644,3
+320,2016,4,Galactosan,2414.362583,2
+320,2016,4,Mannosan,71.433579,1
+320,2016,4,Levoglucosan,0,3
+330,2016,4,Galactosan,2989.97542,2
+330,2016,4,Mannosan,97.213046,1
+330,2016,4,Levoglucosan,201.994761,3
+340,2016,4,Galactosan,2395.086979,2
+340,2016,4,Mannosan,137.177685,1
+340,2016,4,Levoglucosan,377.738894,3
+350,2016,4,Galactosan,2339.550409,2
+350,2016,4,Mannosan,395.783122,1
+350,2016,4,Levoglucosan,2804.271063,3
+360,2016,4,Galactosan,1592.305095,2
+360,2016,4,Mannosan,102.812035,1
+360,2016,4,Levoglucosan,0,3
+370,2016,4,Galactosan,1561.387196,2
+370,2016,4,Mannosan,100.472778,1
+370,2016,4,Levoglucosan,121.453682,3
+380,2016,4,Galactosan,1563.567654,2
+380,2016,4,Mannosan,88.922003,1
+380,2016,4,Levoglucosan,0,3
+390,2016,4,Galactosan,1716.599625,2
+390,2016,4,Mannosan,99.838145,1
+390,2016,4,Levoglucosan,38.634203,3
+400,2016,4,Galactosan,0,2
+400,2016,4,Mannosan,0,1
+400,2016,4,Levoglucosan,0,3
+410,2016,4,Galactosan,931.774896,2
+410,2016,4,Mannosan,0,1
+410,2016,4,Levoglucosan,7677.887747,3
+420,2016,4,Galactosan,1090.18807,2
+420,2016,4,Mannosan,298.112026,1
+420,2016,4,Levoglucosan,3347.288072,3
+430,2016,4,Galactosan,1433.191138,2
+430,2016,4,Mannosan,332.975075,1
+430,2016,4,Levoglucosan,2574.901058,3
+440,2016,4,Galactosan,0,2
+440,2016,4,Mannosan,258.658472,1
+440,2016,4,Levoglucosan,1882.701794,3
+450,2016,4,Galactosan,1255.103989,2
+450,2016,4,Mannosan,227.897359,1
+450,2016,4,Levoglucosan,1650.558476,3
+460,2016,4,Galactosan,2473.151048,2
+460,2016,4,Mannosan,259.486322,1
+460,2016,4,Levoglucosan,1713.806583,3
+470,2016,4,Galactosan,2258.413574,2
+470,2016,4,Mannosan,204.550455,1
+470,2016,4,Levoglucosan,1263.043955,3
+480,2016,4,Galactosan,1516.314454,2
+480,2016,4,Mannosan,211.372062,1
+480,2016,4,Levoglucosan,1109.789235,3
+490,2016,4,Galactosan,1546.450865,2
+490,2016,4,Mannosan,198.339566,1
+490,2016,4,Levoglucosan,1323.657376,3
+500,2016,4,Galactosan,1495.139773,2
+500,2016,4,Mannosan,240.812928,1
+500,2016,4,Levoglucosan,1508.983267,3
+510,2016,4,Galactosan,1403.695931,2
+510,2016,4,Mannosan,157.970099,1
+510,2016,4,Levoglucosan,504.433932,3
+520,2016,4,Galactosan,2269.254272,2
+520,2016,4,Mannosan,130.520594,1
+520,2016,4,Levoglucosan,179.34015,3
+530,2016,4,Galactosan,2395.428393,2
+530,2016,4,Mannosan,127.497076,1
+530,2016,4,Levoglucosan,341.855478,3
+540,2016,4,Galactosan,1474.125533,2
+540,2016,4,Mannosan,134.582946,1
+540,2016,4,Levoglucosan,1125.842934,3
+550,2016,4,Galactosan,869.267239,2
+550,2016,4,Mannosan,121.29928,1
+550,2016,4,Levoglucosan,372.414492,3
+560,2016,4,Galactosan,1087.025224,2
+560,2016,4,Mannosan,523.899157,1
+560,2016,4,Levoglucosan,1148.766409,3
+570,2016,4,Galactosan,1416.989371,2
+570,2016,4,Mannosan,129.76595,1
+570,2016,4,Levoglucosan,420.878992,3
+580,2016,4,Galactosan,1695.694148,2
+580,2016,4,Mannosan,102.702087,1
+580,2016,4,Levoglucosan,149.255382,3
+590,2016,4,Galactosan,1903.530017,2
+590,2016,4,Mannosan,149.314544,1
+590,2016,4,Levoglucosan,0,3
+600,2016,4,Galactosan,1148.072695,2
+600,2016,4,Mannosan,0,1
+600,2016,4,Levoglucosan,2288.673536,3
+610,2016,4,Galactosan,0,2
+610,2016,4,Mannosan,178.257779,1
+610,2016,4,Levoglucosan,450.604782,3
+620,2016,4,Galactosan,1440.308911,2
+620,2016,4,Mannosan,96.453616,1
+620,2016,4,Levoglucosan,62.502273,3
+630,2016,4,Galactosan,0,2
+630,2016,4,Mannosan,92.113134,1
+630,2016,4,Levoglucosan,267.27264,3
+640,2016,4,Galactosan,1381.677762,2
+640,2016,4,Mannosan,0,1
+640,2016,4,Levoglucosan,436.403547,3
+650,2016,4,Galactosan,2166.593594,2
+650,2016,4,Mannosan,88.257043,1
+650,2016,4,Levoglucosan,97.014767,3
+660,2016,4,Galactosan,1751.513998,2
+660,2016,4,Mannosan,106.365504,1
+660,2016,4,Levoglucosan,283.098194,3
+670,2016,4,Galactosan,1718.383503,2
+670,2016,4,Mannosan,101.918201,1
+670,2016,4,Levoglucosan,17.185077,3
+680,2016,4,Galactosan,1279.100684,2
+680,2016,4,Mannosan,89.930546,1
+680,2016,4,Levoglucosan,6.180719,3
+690,2016,4,Galactosan,1977.67112,2
+690,2016,4,Mannosan,0,1
+690,2016,4,Levoglucosan,378.393948,3
+700,2016,4,Galactosan,0,2
+700,2016,4,Mannosan,117.16342,1
+700,2016,4,Levoglucosan,752.055567,3
+710,2016,4,Galactosan,810.601916,2
+710,2016,4,Mannosan,0,1
+710,2016,4,Levoglucosan,1554.414306,3
+720,2016,4,Galactosan,0,2
+720,2016,4,Mannosan,0,1
+720,2016,4,Levoglucosan,962.950967,3
+730,2016,4,Galactosan,0,2
+730,2016,4,Mannosan,0,1
+730,2016,4,Levoglucosan,469.756908,3
+740,2016,4,Galactosan,2142.616368,2
+740,2016,4,Mannosan,106.385285,1
+740,2016,4,Levoglucosan,360.272903,3
+750,2016,4,Galactosan,1764.325741,2
+750,2016,4,Mannosan,154.27015,1
+750,2016,4,Levoglucosan,560.868823,3
+760,2016,4,Galactosan,1941.944339,2
+760,2016,4,Mannosan,0,1
+760,2016,4,Levoglucosan,995.421023,3
+770,2016,4,Galactosan,1850.52928,2
+770,2016,4,Mannosan,91.391607,1
+770,2016,4,Levoglucosan,429.808685,3
diff --git a/src/components/WildfireAerosolsViz.vue b/src/components/WildfireAerosolsViz.vue
index 594bd9c..e26e8e8 100644
--- a/src/components/WildfireAerosolsViz.vue
+++ b/src/components/WildfireAerosolsViz.vue
@@ -38,35 +38,87 @@
 
     // global variables
     const publicPath = import.meta.env.BASE_URL;
-    const dataFile = 'fii_core4data.csv'
-    const data = ref();
+    const tileDataFile = 'fii_core4particulates.csv';
+    const barDataFile = 'fii_core4sugars.csv';
+    const scatterDataFile = 'fii_core4biomass.csv';
+    const tileData = ref();
+    const barData = ref();
+    const scatterData = ref();
     const chart = ref(null);
     const chartTitle = 'Title of chart';
+    const chartHeight = window.innerHeight * 0.8;
+    let chartWidth;
     let chartDimensions;
     let chartBounds;
+    let tileChartDimensions;
+    let tileChartBounds;
+    let tileColorScale;
     // let xScale;
     // let xAxis;
     let yScale;
     let yAxis;
-    let colorScale;
+    let barChartDimensions;
+    let barChartBounds;
+    let barXScale;
+    let barYScale;
+    let barXAxis;
+    const barColors = {Mannosan: '#000000', Galactosan: '#989898', Levoglucosan: '#c8c8c8'};
+    let barColorScale;
+    let scatterChartDimensions;
+    let scatterChartBounds;
+    let scatterXScale;
+    const scatterColors = {grass: '#c49051', hardwood: '#3c475a', softwood: '#729C9D'};
+    let scatterColorScale;
 
     // Behavior on mounted (functions called here)
     // Load data and then make chart
     onMounted(async () => {
         try {
-            await loadDatasets();
+            await loadDatasets({
+                dataFiles: [tileDataFile, barDataFile, scatterDataFile], 
+                dataRefs: [tileData, barData, scatterData],
+                dataNumericFields: [['depth_cm', 'total'], ['picogram_per_mL'], null]
+            });
 
-            if (data.value.length > 0) {
+            if (tileData.value.length > 0 && barData.value.length > 0) {
                 // initialize chart elements
+                chartWidth = chart.value.offsetWidth;
                 initChart({
                     width: chart.value.offsetWidth,
-                    height: window.innerHeight * 0.8,
+                    height: chartHeight,
+                    margin: 10
+                })
+
+                const tileChartWidth = chartWidth / 5
+                initTileChart({
+                    width: tileChartWidth,
+                    height: chartHeight,
                     margin: 10,
                     marginBottom: 50,
                     marginLeft: 100});
 
-                // draw chart
-                drawChart(data.value);
+                const barChartWidth = chartWidth / 3
+                initBarChart({
+                    width: barChartWidth,
+                    height: chartHeight,
+                    margin: 10,
+                    marginBottom: 50,
+                    marginLeft: 80,
+                    translateX: tileChartWidth});
+
+                const scatterChartWidth = chartWidth - tileChartWidth - barChartWidth
+                initScatterChart({
+                    width: scatterChartWidth,
+                    height: chartHeight,
+                    margin: 10,
+                    marginBottom: 50,
+                    marginLeft: 100,
+                    translateX: tileChartWidth + barChartWidth});
+
+                // draw charts
+                drawTileChart(tileData.value);
+                drawBarChart(barData.value);
+                drawScatterChart(scatterData.value)
             } else {
                 console.error('Error loading data');
             }
@@ -75,25 +127,30 @@
         }
     });
 
-    async function loadDatasets() {
+    async function loadDatasets({dataFiles, dataRefs, dataNumericFields}) {
         try {
-            data.value = await loadData(dataFile);
-            console.log('data in');
+            for (let i = 0; i < Math.min(dataFiles.length, dataRefs.length, dataNumericFields.length); i++) {
+                dataRefs[i].value = await loadData(dataFiles[i], dataNumericFields[i]);
+                console.log(`${dataFiles[i]} data in`);
+            }
         } catch (error) {
             console.error('Error loading datasets', error);
         }
     }
 
-    async function loadData(fileName) {
+    async function loadData(dataFile, dataNumericFields) {
         try {
-            const data = await d3.csv(publicPath + fileName, d => {
-                d.depth_cm = +d.depth_cm;
-                d.total = +d.total;
+            const data = await d3.csv(publicPath + dataFile, d => {
+                if (dataNumericFields) {
+                    dataNumericFields.forEach(numericField => {
+                        d[numericField] = +d[numericField]
+                    });
+                }
                 return d;
             });
             return data;
         } catch (error) {
-            console.error(`Error loading data from ${fileName}`, error);
+            console.error(`Error loading data from ${dataFile}`, error);
             return [];
         }
     }
@@ -142,47 +199,183 @@
             }px, ${
                 chartDimensions.margin.top
             }px)`);
+    }
+
+    function initTileChart({
+        width = 500, // outer width, in pixels
+        height = 500, // outer height, in pixels
+        margin = 1, // default margins
+        marginTop = margin, // top margin, in pixels
+        marginBottom = margin, // left margin, in pixels
+        marginLeft = margin, // left margin, in pixels
+        marginRight = margin // right margin, in pixels
+    }) {
+        // set up global chart dimensions, including bounded dimensions
+        tileChartDimensions = {
+            width,
+            height,
+            margin: {
+                top: marginTop,
+                right: marginRight,
+                bottom: marginBottom,
+                left: marginLeft
+            },
+            boundedWidth: width - marginLeft - marginRight,
+            boundedHeight: height - marginTop - marginBottom
+        }
+
+        tileChartBounds = chartBounds.append("g")
+            .attr("id", "tile-chart-bounds")
+            .style("transform", `translate(${
+                tileChartDimensions.margin.left
+            }px, ${
+                tileChartDimensions.margin.top
+            }px)`);
+
 
         // Initialize scales
         // initXScale()
         initYScale()
-        initColorScale()
+        initTileColorScale()
 
         // Initialize axes
-        // initXAxis()
-        initYAxis()
+        initYAxis({bounds: tileChartBounds})
 
         // Add groups for visual elements
-        chartBounds.append("g")
+        tileChartBounds.append("g")
             .attr("class", "rects");
-        chartBounds.append("g")
+        tileChartBounds.append("g")
             .attr("class", "annotations");
     }
 
-    // function initXScale() {
-    //     // scale for x axis (domain set in `drawChart()`)
-    //     xScale = d3.scaleLinear()
-    //         .range([0, chartDimensions.boundedWidth]);
-    // }
+    function initBarChart({
+        width = 500, // outer width, in pixels
+        height = 500, // outer height, in pixels
+        margin = 1, // default margins
+        marginTop = margin, // top margin, in pixels
+        marginBottom = margin, // left margin, in pixels
+        marginLeft = margin, // left margin, in pixels
+        marginRight = margin, // right margin, in pixels
+        translateX = translateX // amount to translate in x direction
+    }) {
+        // set up global chart dimensions, including bounded dimensions
+        barChartDimensions = {
+            width,
+            height,
+            margin: {
+                top: marginTop,
+                right: marginRight,
+                bottom: marginBottom,
+                left: marginLeft
+            },
+            boundedWidth: width - marginLeft - marginRight,
+            boundedHeight: height - marginTop - marginBottom
+        }
+
+        barChartBounds = chartBounds.append("g")
+            .attr("id", "bar-chart-bounds")
+            .style("transform", `translate(${
+                translateX + barChartDimensions.margin.left
+            }px, ${
+                barChartDimensions.margin.top
+            }px)`);
+
+
+        // Initialize scales
+        initBarXScale()
+        initBarYScale()
+
+        // Initialize axes
+        initXAxis({axis: barXAxis, bounds: barChartBounds, chartDims: barChartDimensions})
+
+        // Add groups for visual elements
+        barChartBounds.append("g")
+            .attr("class", "bars");
+    }
+
+    function initScatterChart({
+        width = 500, // outer width, in pixels
+        height = 500, // outer height, in pixels
+        margin = 1, // default margins
+        marginTop = margin, // top margin, in pixels
+        marginBottom = margin, // left margin, in pixels
+        marginLeft = margin, // left margin, in pixels
+        marginRight = margin, // right margin, in pixels
+        translateX = translateX // amount to translate in x direction
+    }) {
+        // set up global chart dimensions, including bounded dimensions
+        scatterChartDimensions = {
+            width,
+            height,
+            margin: {
+                top: marginTop,
+                right: marginRight,
+                bottom: marginBottom,
+                left: marginLeft
+            },
+            boundedWidth: width - marginLeft - marginRight,
+            boundedHeight: height - marginTop - marginBottom
+        }
+
+        scatterChartBounds = chartBounds.append("g")
+            .attr("id", "scatter-chart-bounds")
+            .style("transform", `translate(${
+                translateX + scatterChartDimensions.margin.left
+            }px, ${
+                scatterChartDimensions.margin.top
+            }px)`);
 
-    // function initXAxis() {
-    //     // add group for x axis
-    //     xAxis = chartBounds.append("g")
-    //         .attr("id", "x-axis")
-    //         .attr("class", "axis")
-    //         .attr("transform", `translate(0,${chartDimensions.boundedHeight})`)
-    //         .attr("aria-hidden", true); // hide from screen reader
-    // }
+
+        // Initialize scales
+        initScatterXScale()
+
+        // Add groups for visual elements
+        scatterChartBounds.append("g")
+            .attr("class", "points");
+    }
+
+    function initBarXScale() {
+        // scale for x axis (domain set in `drawBarChart()`)
+        barXScale = d3.scaleLinear()
+            .range([0, barChartDimensions.boundedWidth]);
+    }
+
+    function initBarYScale() {
+        // scale for the y axis (domain set in `drawBarChart()`)
+        barYScale = d3.scaleBand()
+            .range([0, tileChartDimensions.boundedHeight]);
+    }
+
+    function initScatterXScale() {
+        // scale for the x axis (domain set in `drawScatterChart()`)
+        scatterXScale = d3.scaleBand()
+            .range([0, scatterChartDimensions.boundedWidth]);
+    }
+
+    function initXAxis({
+        axis,
+        bounds,
+        chartDims
+    }) {
+        // add group for x axis
+        axis = bounds.append("g")
+            .attr("id", "x-axis")
+            .attr("class", "axis")
+            .attr("transform", `translate(0,${chartDims.boundedHeight})`)
+            .attr("aria-hidden", true); // hide from screen reader
+    }
 
     function initYScale() {
-        // scale for the y axis (domain set in `drawChart()`)
+        // scale for the y axis (domain set in `drawTileChart()`)
         yScale = d3.scaleLinear()
-            .range([0, chartDimensions.boundedHeight]);
+            .range([0, tileChartDimensions.boundedHeight]);
     }
 
-    function initYAxis() {
+    function initYAxis({
+        bounds
+    }) {
         // add group for y axis
-        yAxis = chartBounds.append("g")
+        yAxis = bounds.append("g")
             .attr("id", "y-axis")
             .attr("class", "axis")
             .attr("aria-hidden", true);
@@ -191,11 +384,12 @@
     function drawAxis({
         axis,
         axisScale,
-        axisFxn
+        axisFxn,
+        chartDims
     }, {
         axisTitle = '',
-        titleX = -chartDimensions.boundedHeight / 2,
-        titleY = -chartDimensions.margin.left,
+        titleX = -chartDims.boundedHeight / 2,
+        titleY = -chartDims.margin.left,
         titleTextAnchor = "middle",
         titleBaseline = "text-before-edge",
         titleAngle = -90,
@@ -246,8 +440,8 @@
 
     // function drawXAxis({
     //     axisTitle = '',
-    //     titleX = chartDimensions.boundedWidth / 2,
-    //     titleY = chartDimensions.margin.bottom,
+    //     titleX = tileChartDimensions.boundedWidth / 2,
+    //     titleY = tileChartDimensions.margin.bottom,
     //     titleTextAnchor = "middle",
     //     titleBaseline = "text-after-edge",
     //     titleAngle = 0,
@@ -280,9 +474,13 @@
     // }
 
     function drawYAxis({
+        axis,
+        axisScale,
+        chartDims
+    }, {
         axisTitle = '',
-        titleX = -chartDimensions.boundedHeight / 2,
-        titleY = -chartDimensions.margin.left,
+        titleX = -chartDims.boundedHeight / 2,
+        titleY = -chartDims.margin.left,
         titleTextAnchor = "middle",
         titleBaseline = "text-before-edge",
         titleAngle = -90,
@@ -295,9 +493,10 @@
         keepDomain = true,
     }) {
         drawAxis({
-            axis: yAxis,
-            axisScale: yScale,
-            axisFxn: 'axisLeft'
+            axis: axis,
+            axisScale: axisScale,
+            axisFxn: 'axisLeft',
+            chartDims: chartDims
         }, {
             axisTitle: axisTitle,
             titleX: titleX,
@@ -315,12 +514,24 @@
         })
     }
 
-    function initColorScale() {
-        colorScale = d3.scaleSequential()           
+    function initTileColorScale() {
+        tileColorScale = d3.scaleSequential()           
             .interpolator(d3.interpolateGreys);
     }
 
-    function drawChart(data) {
+    function initBarColorScale(data) {
+        barColorScale = d3.scaleOrdinal()
+            .domain(data)
+            .range(data.map(item => barColors[item]));
+    }
+
+    function initScatterColorScale(data) {
+        scatterColorScale = d3.scaleOrdinal()
+            .domain(data)
+            .range(data.map(item => scatterColors[item]));
+    }
+
+    function drawTileChart(data) {
         //////////////////////////////
         /////    PROCESS DATA    /////
         //////////////////////////////
@@ -339,71 +550,180 @@
         // set domain for yScale, based on data
         yScale
             .domain([0, d3.max(data, yAccessor) + 10]);
-        drawYAxis({tickFormat: ".0f", customSuffix: 'cm', tickSize: 3, keepDomain: false})
-        
-        ///////////////////////////////////////////
-        /////    FINISH SETTING UP X SCALE    /////
-        ///////////////////////////////////////////
-        // // set domain for xScale
-        // xScale
-        //     .domain([0, d3.max(data, xAccessor)]);
-        // drawXAxis({axisTitle: 'Climate vulnerability'})
+        drawYAxis(
+            {axis: yAxis, axisScale: yScale, chartDims: tileChartDimensions}, 
+            {tickFormat: ".0f", customSuffix: 'cm', tickSize: 3, keepDomain: false}
+        )
 
         ///////////////////////////////////
         /////    SET UP COLOR SCALE   /////
         ///////////////////////////////////
-        colorScale
+        tileColorScale
             .domain(d3.extent(data, colorAccessor));
 
         ////////////////////////////////////
         /////    ADD CHART ELEMENTS    /////
         ////////////////////////////////////
+        const annotationGap = 40;
+        const annotationBuffer = 5;
         // draw chart
-        chartBounds.select('.rects') // selects our group we set up to hold chart elements
+        tileChartBounds.select('.rects') // selects our group we set up to hold chart elements
             .selectAll(".rect") // empty selection
                 .data(data) // bind data
                 .enter() // instantiate chart element for each element of data
                 .append("rect") // append a rectangle for each element
                     .attr("class", "rect")
                     .attr("id", d => 'bar-' + identifierAccessor(d))
-                    .attr("x", 40)
+                    .attr("x", annotationGap)
                     .attr("y", d => yScale(yAccessor(d)))
-                    .attr("height", chartDimensions.boundedHeight / data.length)
-                    .attr("width", 50)
-                    .style("fill", d => colorScale(colorAccessor(d)));
+                    .attr("height", tileChartDimensions.boundedHeight / data.length)
+                    .attr("width", tileChartDimensions.boundedWidth - annotationGap)
+                    .style("fill", d => tileColorScale(colorAccessor(d)));
         // draw year bands
-        chartBounds.select(".annotations")
+        tileChartBounds.select(".annotations")
             .append("rect")
                 .attr("class", "year-bands")
-                .attr("x", 25)
+                .attr("x", annotationGap / 2 + annotationBuffer)
                 .attr("y", 0)
                 .attr("height", yScale(372))
                 .attr("width", 3)
 
-        chartBounds.select(".annotations")
+        tileChartBounds.select(".annotations")
             .append("rect")
                 .attr("class", "year-bands")
-                .attr("x", 25)
+                .attr("x", annotationGap / 2 + annotationBuffer)
                 .attr("y", yScale(378))
-                .attr("height", chartDimensions.boundedHeight - yScale(378))
+                .attr("height", tileChartDimensions.boundedHeight - yScale(378))
                 .attr("width", 3)
 
-        chartBounds.select(".annotations")
+        tileChartBounds.select(".annotations")
             .append("text")
                 .attr("x", - yScale(372) / 2)
-                .attr("y", 20)
+                .attr("y", annotationGap / 2)
                 .attr("transform", "rotate(-90)")
                 .attr("text-anchor", "middle")
                 .text("2016 accumulation")
         
-        chartBounds.select(".annotations")
+        tileChartBounds.select(".annotations")
             .append("text")
-                .attr("x", - yScale(378) - ((chartDimensions.boundedHeight - yScale(378)) / 2))
-                .attr("y", 20)
+                .attr("x", - yScale(378) - ((tileChartDimensions.boundedHeight - yScale(378)) / 2))
+                .attr("y", annotationGap / 2)
                 .attr("transform", "rotate(-90)")
                 .attr("text-anchor", "middle")
                 .text("2015 accumulation")
     }
+
+    function drawBarChart(data) {
+        ///////////////////////////////////////////
+        /////    SET UP ACCESSOR FUNCTIONS    /////
+        ///////////////////////////////////////////
+        const yAccessor = d => d.depth_cm;
+        const xAccessor = d => d.picogram_per_mL;
+        const keyAccessor = d => d.sugar;
+        const colorAccessor = d => d.sugar;
+
+        //////////////////////////////
+        /////    PROCESS DATA    /////
+        //////////////////////////////
+        // sort data by bar order, so species with shared color plot together
+        data.sort((a,b) => (a.bar_order > b.bar_order) ? 1 : ((b.bar_order > a.bar_order) ? -1 : 0))
+        const series = d3.stack()
+            .keys(d3.union(data.map(d => keyAccessor(d)))) // distinct series keys, in input order
+            .value(([, D], key) => xAccessor(D.get(key))) // get value for each series key and stack
+            .order(d3.stackOrderNone)
+            (d3.index(data, d => yAccessor(d), d => keyAccessor(d)));
+
+        ///////////////////////////////////////////
+        /////    FINISH SETTING UP Y SCALE    /////
+        ///////////////////////////////////////////
+        // set domain for yScale, based on data
+        barYScale
+            .domain([... new Set(data.map(d => yAccessor(d)))]);
+        
+        ///////////////////////////////////////////
+        /////    FINISH SETTING UP X SCALE    /////
+        ///////////////////////////////////////////
+        // // set domain for xScale
+        barXScale
+            .domain([0, d3.max(series, d => d3.max(d, d => d[1]))]);
+        // drawXAxis({axisTitle: 'Climate vulnerability'})
+        
+        ///////////////////////////////////
+        /////    SET UP COLOR SCALE   /////
+        ///////////////////////////////////
+        const colorCategories = [... new Set(data.map(colorAccessor))];
+        initBarColorScale(colorCategories)
+
+        ////////////////////////////////////
+        /////    ADD CHART ELEMENTS    /////
+        ////////////////////////////////////
+        // draw chart
+        const barGroups = barChartBounds.selectAll('.bars')
+            .selectAll(".bar") // empty selection
+            .data(series)
+            .enter()
+            .append("g")
+                .attr("class", d => d.key)
+        
+        barGroups.selectAll(".bar") //empty selection
+            .data(D => D.map(d => (d.key = D.key, d)))
+            .enter()
+            .append("rect")
+                .attr("class", d => d.key + ' depth' + d.data[0])
+                .attr("y", d => barYScale(d.data[0]))
+                .attr("x", d => barXScale(d[0]))
+                .attr("height", barYScale.bandwidth())
+                .attr("width", d => barXScale(d[1]) - barXScale(d[0]))
+                .style("fill", d => barColorScale(d.key))
+    }
+
+    function drawScatterChart(data) {
+        //////////////////////////////
+        /////    PROCESS DATA    /////
+        //////////////////////////////
+
+        ///////////////////////////////////////////
+        /////    SET UP ACCESSOR FUNCTIONS    /////
+        ///////////////////////////////////////////
+        const yAccessor = d => d.depth_cm;
+        const xAccessor = d => d.vegetation_type;
+        const colorAccessor = d => d.vegetation_type;
+        const identifierAccessor = d => 'depth-' + d.depth_cm + '-' + d.vegetation_type;
+
+        ///////////////////////////////////////////
+        /////    FINISH SETTING UP Y SCALE    /////
+        ///////////////////////////////////////////
+        // use bar chart y scale
+        
+        ///////////////////////////////////////////
+        /////    FINISH SETTING UP X SCALE    /////
+        ///////////////////////////////////////////
+        // set domain for xScale
+        scatterXScale
+            .domain([... new Set(data.map(d => xAccessor(d)))]);
+
+        ///////////////////////////////////
+        /////    SET UP COLOR SCALE   /////
+        ///////////////////////////////////
+        const colorCategories = [... new Set(data.map(colorAccessor))];
+        initScatterColorScale(colorCategories)
+
+        ////////////////////////////////////
+        /////    ADD CHART ELEMENTS    /////
+        ////////////////////////////////////
+        // draw chart
+        scatterChartBounds.select('.points') // selects our group we set up to hold chart elements
+            .selectAll(".point") // empty selection
+                .data(data) // bind data
+                .enter() // instantiate chart element for each element of data
+                .append("circle") // append a rectangle for each element
+                    .attr("class", "point")
+                    .attr("id", d => 'point-' + identifierAccessor(d))
+                    .attr("cx", d => scatterXScale(xAccessor(d)))
+                    .attr("cy", d => barYScale(yAccessor(d)) + barYScale.bandwidth()/2)
+                    .attr("r", 4)
+                    .style("fill", d => scatterColorScale(colorAccessor(d)));
+    }
 </script>
 
 <style scoped lang="scss">
-- 
GitLab


From 1149dfb4d2b5c3bc197f25f8bb3c32580d12e376 Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Wed, 23 Oct 2024 15:51:11 -0500
Subject: [PATCH 06/38] set up grid and buttons

---
 src/assets/text/text.js                |   7 +-
 src/components/WildfireAerosolsViz.vue | 130 ++++++++++++++++++++++---
 2 files changed, 120 insertions(+), 17 deletions(-)

diff --git a/src/assets/text/text.js b/src/assets/text/text.js
index 9a3150c..26f586f 100644
--- a/src/assets/text/text.js
+++ b/src/assets/text/text.js
@@ -177,10 +177,9 @@ export default {
             paragraph1: 'Explore total recreational harvest for the five families of inland fish with the largest recreational harvests: <span class="scientificName">Cyprinidae</span> (minnows and carps), <span class="scientificName">Percidae</span> (perch), <span class="scientificName">Salmonidae</span> (salmon, trout, grayling, and whitefish), <span class="scientificName">Bagridae</span> (bagrid catfish), and <span class="scientificName">Centrarchidae</span> (sunfishes). Total recreational harvest is broken out by family, by species, and by country.  Hover over the chart to see the harvest totals, in kilograms'
         },
         WildfireAerosols: {
-            paragraph1: "In the cores, we see that there is more than just ice. Particles from the air, called aerosols, have been deposited in the ice.",
-            paragraph2: "These aerosols can come from dust, fossil fuel combustion, or wildfires.",
-            paragraph3: "What are the particles made of? These L, M, and G sugars indicate that some of the particules have been deposited by wildfires.",   
-            paragraph4: "Some L/(M+G) peaks indicate that hardwoods were burned, which suggests a further source. Based on hardwood forest locations, one likely source is East Asia."
+            paragraph1: "In the cores, we see that there is more than just ice. Particles from the air, called aerosols, have been deposited in the ice. These aerosols can come from dust, fossil fuel combustion, or wildfires.",
+            paragraph2: "What are the particles made of? These L, M, and G sugars indicate that some of the particules have been deposited by wildfires.",   
+            paragraph3: "Some L/(M+G) peaks indicate that hardwoods were burned, which suggests a further source. Based on hardwood forest locations, one likely source is East Asia."
         },
         ThreatSankey: {
             paragraph1: 'Land use change is the biggest threat to inland fisheries.'
diff --git a/src/components/WildfireAerosolsViz.vue b/src/components/WildfireAerosolsViz.vue
index e26e8e8..77f7c9c 100644
--- a/src/components/WildfireAerosolsViz.vue
+++ b/src/components/WildfireAerosolsViz.vue
@@ -10,14 +10,19 @@
                 {{ text.heading }}
             </h2>
         </template>
-        <template #aboveExplanation>
-            <p v-html="text.paragraph1" />
-            <p v-html="text.paragraph2" />
-            <p v-html="text.paragraph3" />
-            <p v-html="text.paragraph4" />
-        </template>
         <template #figures>
-            <div id="chart-container" class="maxWidth" ref="chart"></div>
+            <div id="wildfire-aerosols-grid-container">
+                <button id="aerosol-prev" class="flip-button" @click="currentIndex--" :disabled="isFirstImage">
+                    <font-awesome-icon :icon="{ prefix: 'fas', iconName: 'arrow-left' }"  class="fa fa-arrow-left"/>
+                </button>
+                <button id="aerosol-next" class="flip-button" @click="currentIndex++" :disabled="isLastImage">
+                    <font-awesome-icon :icon="{ prefix: 'fas', iconName: 'arrow-right' }"  class="fa fa-arrow-right"/>
+                </button>
+                <div id="aerosol-text-container" class="text-container">
+                    <p v-html="currentText" />
+                </div>
+                <div id="chart-container" ref="chart"></div>
+            </div>
         </template>
         <template #figureCaption>
         </template>
@@ -27,12 +32,12 @@
 </template>
 
 <script setup>
-    import { onMounted, ref } from "vue";
+    import { computed, onMounted, ref } from "vue";
     import * as d3 from 'd3';
     import VizSection from '@/components/VizSection.vue';
 
     // define props
-    defineProps({
+    const props = defineProps({
         text: { type: Object }
     })
 
@@ -44,6 +49,8 @@
     const tileData = ref();
     const barData = ref();
     const scatterData = ref();
+    const currentIndex = ref(1);
+    const nIndices = 3;
     const chart = ref(null);
     const chartTitle = 'Title of chart';
     const chartHeight = window.innerHeight * 0.8;
@@ -70,6 +77,17 @@
     const scatterColors = {grass: '#c49051', hardwood: '#3c475a', softwood: '#729C9D'};
     let scatterColorScale;
 
+    const isFirstImage = computed(() => {
+        return currentIndex.value === 1;
+    });
+    const isLastImage = computed(() => {
+        return currentIndex.value === nIndices;
+    });
+    const currentText = computed(() => {
+        const selectionString = 'paragraph' + currentIndex.value
+        return props.text[selectionString];
+    });
+
     // Behavior on mounted (functions called here)
     // Load data and then make chart
     onMounted(async () => {
@@ -89,7 +107,7 @@
                     margin: 10
                 })
 
-                const tileChartWidth = chartWidth / 5
+                const tileChartWidth = chartWidth / 3
                 initTileChart({
                     width: tileChartWidth,
                     height: chartHeight,
@@ -112,7 +130,7 @@
                     height: chartHeight,
                     margin: 10,
                     marginBottom: 50,
-                    marginLeft: 100,
+                    marginLeft: 60,
                     translateX: tileChartWidth + barChartWidth});
 
                 // draw charts
@@ -564,8 +582,8 @@
         ////////////////////////////////////
         /////    ADD CHART ELEMENTS    /////
         ////////////////////////////////////
-        const annotationGap = 40;
-        const annotationBuffer = 5;
+        const annotationGap = tileChartDimensions.boundedWidth * 0.5;
+        const annotationBuffer = annotationGap * 0.2;
         // draw chart
         tileChartBounds.select('.rects') // selects our group we set up to hold chart elements
             .selectAll(".rect") // empty selection
@@ -727,6 +745,92 @@
 </script>
 
 <style scoped lang="scss">
+    #wildfire-aerosols-grid-container {
+        display: grid;
+        max-width: 1200px;
+        grid-template-columns: 10% calc(80% - 4rem) 10%;
+        grid-template-rows: auto max-content;
+        grid-template-areas:
+            "text text text"
+            "prev chart next";
+        margin: 2rem auto 0 auto;
+        column-gap: 2rem;
+        row-gap: 3rem;
+        @media only screen and (max-width: 600px) {
+            width: 90vw;
+            grid-template-rows: auto max-content;
+            grid-template-areas:
+                "chart chart chart"
+                "prev text next";
+        }
+    }
+    #chart-container {
+        grid-area: chart;
+        width: 100%;
+    }
+    #aerosol-text-container {
+        grid-area: text;
+        height: 10vh;
+        @media screen and (max-height: 770px) {
+            height: 30vh;
+        }
+        @media only screen and (max-width: 600px) {
+            height: auto;
+        }
+    }
+    .flip-button {
+        height: 5rem;
+        width: 5rem;
+        align-self: center;
+        border-radius: 5rem;
+        border: solid 0.75px var(--medium-grey);
+        cursor: pointer;
+        box-shadow: 0px 0px 4px rgba(39,44,49,.3);
+        @media only screen and (max-width: 600px) {
+            height: 3rem;
+            width: 3rem;
+            align-self: start;
+        }
+    }
+    #aerosol-prev {
+        grid-area: prev;
+        justify-self: end;
+    }
+    #aerosol-next {
+        grid-area: next;
+        justify-self: start;
+    }
+    button:hover:after {
+        top: 0px;
+        left: 0px;
+    }
+    button:hover {
+        box-shadow: rgba(39,44,49,.7) 2px 2px 4px -2px;
+        transform: translate3d(0, 2px, 0);
+    }
+    button:disabled {
+        background-color: #b4b2b2;
+        cursor: not-allowed;
+        border-color: #b4b2b2;
+        color: #b4b2b2;
+    }
+    button:disabled:hover {
+        box-shadow: 0px 0px 4px rgba(39,44,49,.3);
+        transform: translate3d(0, 0px, 0);
+    }
+    button:disabled:after {
+        background-color: #b4b2b2;
+    }
+    .fa {
+        color: var(--color-text);
+        opacity: 1;
+        @media only screen and (max-width: 600px) {
+            font-size: 1.6rem;
+        }
+    }
+    button:disabled .fa {
+        opacity: 0.2;
+    }
 </style>
 <style lang="scss">
 /* css for elements added/classed w/ d3 */
-- 
GitLab


From 17407495bb3ac91e59dfed07760efca6586ea278 Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Wed, 23 Oct 2024 15:51:31 -0500
Subject: [PATCH 07/38] fix typo

---
 src/components/BeaufortSeaCoreViz.vue | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/components/BeaufortSeaCoreViz.vue b/src/components/BeaufortSeaCoreViz.vue
index 280094f..ae471b6 100644
--- a/src/components/BeaufortSeaCoreViz.vue
+++ b/src/components/BeaufortSeaCoreViz.vue
@@ -14,7 +14,7 @@
                 <h2 v-html="text.heading2" />
             </template>
         </VizSection>
-        <div id="ice-coring-grid-container">
+        <div id="sediment-coring-grid-container">
             <div id="coring-image-container">
                 <img class="coring-image" :src="currentImage" :alt="currentAltText">
             </div>
@@ -71,7 +71,7 @@
 </script>
 
 <style>
-    #ice-coring-grid-container{
+    #sediment-coring-grid-container{
         display: grid;
         max-width: 1200px;
         grid-template-columns: 10% calc(80% - 4rem) 10%;
-- 
GitLab


From 790e23ba5258fb3eba348a7c7141e3152ef4ef2d Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Wed, 23 Oct 2024 15:58:24 -0500
Subject: [PATCH 08/38] clean up grid, adjust mobile

---
 src/components/WildfireAerosolsViz.vue | 27 +++++++++++++-------------
 1 file changed, 14 insertions(+), 13 deletions(-)

diff --git a/src/components/WildfireAerosolsViz.vue b/src/components/WildfireAerosolsViz.vue
index 77f7c9c..d58d431 100644
--- a/src/components/WildfireAerosolsViz.vue
+++ b/src/components/WildfireAerosolsViz.vue
@@ -33,6 +33,7 @@
 
 <script setup>
     import { computed, onMounted, ref } from "vue";
+    import { isMobile } from 'mobile-device-detect';
     import * as d3 from 'd3';
     import VizSection from '@/components/VizSection.vue';
 
@@ -43,6 +44,7 @@
 
     // global variables
     const publicPath = import.meta.env.BASE_URL;
+    const mobileView = isMobile;
     const tileDataFile = 'fii_core4particulates.csv';
     const barDataFile = 'fii_core4sugars.csv';
     const scatterDataFile = 'fii_core4biomass.csv';
@@ -53,7 +55,7 @@
     const nIndices = 3;
     const chart = ref(null);
     const chartTitle = 'Title of chart';
-    const chartHeight = window.innerHeight * 0.8;
+    const chartHeight = mobileView ? window.innerHeight * 0.6 : window.innerHeight * 0.8;
     let chartWidth;
     let chartDimensions;
     let chartBounds;
@@ -104,33 +106,30 @@
                 initChart({
                     width: chart.value.offsetWidth,
                     height: chartHeight,
-                    margin: 10
+                    margin: mobileView ? 5 : 10
                 })
 
                 const tileChartWidth = chartWidth / 3
                 initTileChart({
                     width: tileChartWidth,
                     height: chartHeight,
-                    margin: 10,
-                    marginBottom: 50,
-                    marginLeft: 100});
+                    margin: mobileView ? 5 : 10,
+                    marginLeft: mobileView ? 60: 100});
 
                 const barChartWidth = chartWidth / 3
                 initBarChart({
                     width: barChartWidth,
                     height: chartHeight,
-                    margin: 10,
-                    marginBottom: 50,
-                    marginLeft: 80,
+                    margin: mobileView ? 5 : 10,
+                    marginLeft: mobileView ? 20 : 80,
                     translateX: tileChartWidth});
 
                 const scatterChartWidth = chartWidth - tileChartWidth - barChartWidth
                 initScatterChart({
                     width: scatterChartWidth,
                     height: chartHeight,
-                    margin: 10,
-                    marginBottom: 50,
-                    marginLeft: 60,
+                    margin: mobileView ? 5 : 10,
+                    marginLeft: mobileView ? 20 : 60,
                     translateX: tileChartWidth + barChartWidth});
 
                 // draw charts
@@ -751,8 +750,8 @@
         grid-template-columns: 10% calc(80% - 4rem) 10%;
         grid-template-rows: auto max-content;
         grid-template-areas:
-            "text text text"
-            "prev chart next";
+            "prev text next"
+            "chart chart chart";
         margin: 2rem auto 0 auto;
         column-gap: 2rem;
         row-gap: 3rem;
@@ -795,10 +794,12 @@
     #aerosol-prev {
         grid-area: prev;
         justify-self: end;
+        align-self: start;
     }
     #aerosol-next {
         grid-area: next;
         justify-self: start;
+        align-self: start;
     }
     button:hover:after {
         top: 0px;
-- 
GitLab


From b19580aed7853fe6daa2fcee5fd313c107883731 Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Thu, 24 Oct 2024 14:25:54 -0500
Subject: [PATCH 09/38] Start working on indexing and transitions

---
 src/components/WildfireAerosolsViz.vue | 72 ++++++++++++++++++--------
 1 file changed, 51 insertions(+), 21 deletions(-)

diff --git a/src/components/WildfireAerosolsViz.vue b/src/components/WildfireAerosolsViz.vue
index d58d431..a03b3c6 100644
--- a/src/components/WildfireAerosolsViz.vue
+++ b/src/components/WildfireAerosolsViz.vue
@@ -32,7 +32,7 @@
 </template>
 
 <script setup>
-    import { computed, onMounted, ref } from "vue";
+    import { computed, onMounted, ref, watch } from "vue";
     import { isMobile } from 'mobile-device-detect';
     import * as d3 from 'd3';
     import VizSection from '@/components/VizSection.vue';
@@ -59,6 +59,7 @@
     let chartWidth;
     let chartDimensions;
     let chartBounds;
+    let tileChartDrawn = ref(false);
     let tileChartDimensions;
     let tileChartBounds;
     let tileColorScale;
@@ -90,6 +91,10 @@
         return props.text[selectionString];
     });
 
+    watch(currentIndex, () => {
+        updateChartView(currentIndex.value)
+    })
+
     // Behavior on mounted (functions called here)
     // Load data and then make chart
     onMounted(async () => {
@@ -109,33 +114,37 @@
                     margin: mobileView ? 5 : 10
                 })
 
-                const tileChartWidth = chartWidth / 3
+                // updateChartView(currentIndex.value)
+
+                const tileChartWidth = chartDimensions.boundedWidth
                 initTileChart({
                     width: tileChartWidth,
                     height: chartHeight,
                     margin: mobileView ? 5 : 10,
-                    marginLeft: mobileView ? 60: 100});
-
-                const barChartWidth = chartWidth / 3
-                initBarChart({
-                    width: barChartWidth,
-                    height: chartHeight,
-                    margin: mobileView ? 5 : 10,
-                    marginLeft: mobileView ? 20 : 80,
-                    translateX: tileChartWidth});
-
-                const scatterChartWidth = chartWidth - tileChartWidth - barChartWidth
-                initScatterChart({
-                    width: scatterChartWidth,
-                    height: chartHeight,
-                    margin: mobileView ? 5 : 10,
-                    marginLeft: mobileView ? 20 : 60,
-                    translateX: tileChartWidth + barChartWidth});
+                    marginLeft: mobileView ? 60: chartDimensions.boundedWidth / 3,
+                    marginRight: mobileView ? 60: chartDimensions.boundedWidth / 3
+                });
+
+                // const barChartWidth = chartWidth / 3
+                // initBarChart({
+                //     width: barChartWidth,
+                //     height: chartHeight,
+                //     margin: mobileView ? 5 : 10,
+                //     marginLeft: mobileView ? 20 : 80,
+                //     translateX: tileChartWidth});
+
+                // const scatterChartWidth = chartWidth - tileChartWidth - barChartWidth
+                // initScatterChart({
+                //     width: scatterChartWidth,
+                //     height: chartHeight,
+                //     margin: mobileView ? 5 : 10,
+                //     marginLeft: mobileView ? 20 : 60,
+                //     translateX: tileChartWidth + barChartWidth});
 
                 // draw charts
                 drawTileChart(tileData.value);
-                drawBarChart(barData.value);
-                drawScatterChart(scatterData.value)
+                // drawBarChart(barData.value);
+                // drawScatterChart(scatterData.value);
             } else {
                 console.error('Error loading data');
             }
@@ -628,6 +637,8 @@
                 .attr("transform", "rotate(-90)")
                 .attr("text-anchor", "middle")
                 .text("2015 accumulation")
+
+        tileChartDrawn.value = true;
     }
 
     function drawBarChart(data) {
@@ -741,6 +752,25 @@
                     .attr("r", 4)
                     .style("fill", d => scatterColorScale(colorAccessor(d)));
     }
+
+    function updateChartView(index) {
+        if (index == 1) {
+            console.log('1');
+        } else if (index == 2) {
+            tileChartDimensions.margin.left = chartDimensions.boundedWidth / 5
+            tileChartBounds
+                .transition()
+                .duration(2000)
+                .style("transform", `translate(${
+                    tileChartDimensions.margin.left
+                }px, ${
+                    tileChartDimensions.margin.top
+                }px)`);
+            console.log(2)
+        } else if (index == 3) {
+            console.log(3)
+        }
+    }
 </script>
 
 <style scoped lang="scss">
-- 
GitLab


From 432e3ba1b7f791941b2ff6a465aa6b864cf1cca5 Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Thu, 24 Oct 2024 23:54:25 -0500
Subject: [PATCH 10/38] get translations working

---
 src/components/WildfireAerosolsViz.vue | 143 +++++++++++++++++--------
 1 file changed, 101 insertions(+), 42 deletions(-)

diff --git a/src/components/WildfireAerosolsViz.vue b/src/components/WildfireAerosolsViz.vue
index a03b3c6..500ae52 100644
--- a/src/components/WildfireAerosolsViz.vue
+++ b/src/components/WildfireAerosolsViz.vue
@@ -12,10 +12,10 @@
         </template>
         <template #figures>
             <div id="wildfire-aerosols-grid-container">
-                <button id="aerosol-prev" class="flip-button" @click="currentIndex--" :disabled="isFirstImage">
+                <button id="aerosol-prev" class="flip-button" @click="currentIndex--; clicked()" :disabled="isFirstImage || justClicked">
                     <font-awesome-icon :icon="{ prefix: 'fas', iconName: 'arrow-left' }"  class="fa fa-arrow-left"/>
                 </button>
-                <button id="aerosol-next" class="flip-button" @click="currentIndex++" :disabled="isLastImage">
+                <button id="aerosol-next" class="flip-button" @click="currentIndex++; clicked()" :disabled="isLastImage || justClicked">
                     <font-awesome-icon :icon="{ prefix: 'fas', iconName: 'arrow-right' }"  class="fa fa-arrow-right"/>
                 </button>
                 <div id="aerosol-text-container" class="text-container">
@@ -52,6 +52,7 @@
     const barData = ref();
     const scatterData = ref();
     const currentIndex = ref(1);
+    const justClicked = ref(false);
     const nIndices = 3;
     const chart = ref(null);
     const chartTitle = 'Title of chart';
@@ -59,7 +60,9 @@
     let chartWidth;
     let chartDimensions;
     let chartBounds;
-    let tileChartDrawn = ref(false);
+    let tileChartTranslateX1;
+    let tileChartTranslateX2;
+    let tileChartTranslateX3;
     let tileChartDimensions;
     let tileChartBounds;
     let tileColorScale;
@@ -67,6 +70,8 @@
     // let xAxis;
     let yScale;
     let yAxis;
+    let barChartTranslateX2;
+    let barChartTranslateX3;
     let barChartDimensions;
     let barChartBounds;
     let barXScale;
@@ -74,11 +79,13 @@
     let barXAxis;
     const barColors = {Mannosan: '#000000', Galactosan: '#989898', Levoglucosan: '#c8c8c8'};
     let barColorScale;
+    let scatterChartTranslateX3;
     let scatterChartDimensions;
     let scatterChartBounds;
     let scatterXScale;
     const scatterColors = {grass: '#c49051', hardwood: '#3c475a', softwood: '#729C9D'};
     let scatterColorScale;
+    const transitionLength = 2000;
 
     const isFirstImage = computed(() => {
         return currentIndex.value === 1;
@@ -109,42 +116,51 @@
                 // initialize chart elements
                 chartWidth = chart.value.offsetWidth;
                 initChart({
-                    width: chart.value.offsetWidth,
+                    width: chartWidth,
                     height: chartHeight,
                     margin: mobileView ? 5 : 10
                 })
 
-                // updateChartView(currentIndex.value)
-
-                const tileChartWidth = chartDimensions.boundedWidth
+                const tileChartWidth = chartDimensions.boundedWidth / 3
+                tileChartTranslateX1 = 350;
+                tileChartTranslateX2 = 200;
+                tileChartTranslateX3 = 80;
                 initTileChart({
                     width: tileChartWidth,
                     height: chartHeight,
                     margin: mobileView ? 5 : 10,
-                    marginLeft: mobileView ? 60: chartDimensions.boundedWidth / 3,
-                    marginRight: mobileView ? 60: chartDimensions.boundedWidth / 3
+                    marginLeft: mobileView ? 60: 80,
+                    marginRight: mobileView ? 60: 40,
+                    translateX: tileChartTranslateX1
                 });
 
-                // const barChartWidth = chartWidth / 3
-                // initBarChart({
-                //     width: barChartWidth,
-                //     height: chartHeight,
-                //     margin: mobileView ? 5 : 10,
-                //     marginLeft: mobileView ? 20 : 80,
-                //     translateX: tileChartWidth});
-
-                // const scatterChartWidth = chartWidth - tileChartWidth - barChartWidth
-                // initScatterChart({
-                //     width: scatterChartWidth,
-                //     height: chartHeight,
-                //     margin: mobileView ? 5 : 10,
-                //     marginLeft: mobileView ? 20 : 60,
-                //     translateX: tileChartWidth + barChartWidth});
+                const barChartWidth = chartDimensions.boundedWidth / 3
+                barChartTranslateX2 = tileChartWidth + 150;
+                barChartTranslateX3 = tileChartWidth + 80;
+                initBarChart({
+                    width: barChartWidth,
+                    height: chartHeight,
+                    margin: mobileView ? 5 : 10,
+                    marginLeft: mobileView ? 20 : 80,
+                    translateX: barChartTranslateX2});
+
+                const scatterChartWidth = chartDimensions.boundedWidth - tileChartWidth - barChartWidth
+                scatterChartTranslateX3 = tileChartWidth + barChartWidth + 50;
+                initScatterChart({
+                    width: scatterChartWidth,
+                    height: chartHeight,
+                    margin: mobileView ? 5 : 10,
+                    marginLeft: mobileView ? 20 : 60,
+                    translateX: scatterChartTranslateX3});
 
                 // draw charts
                 drawTileChart(tileData.value);
-                // drawBarChart(barData.value);
-                // drawScatterChart(scatterData.value);
+                drawBarChart(barData.value);
+                barChartBounds
+                    .attr("display", "none");
+                drawScatterChart(scatterData.value);
+                scatterChartBounds
+                    .attr("display", "none");
             } else {
                 console.error('Error loading data');
             }
@@ -181,6 +197,11 @@
         }
     }
 
+    function clicked() {
+        justClicked.value = true;
+        setTimeout(function(){justClicked.value = false;}, transitionLength);
+    }
+
     function initChart({
         width = 500, // outer width, in pixels
         height = 500, // outer height, in pixels
@@ -234,7 +255,8 @@
         marginTop = margin, // top margin, in pixels
         marginBottom = margin, // left margin, in pixels
         marginLeft = margin, // left margin, in pixels
-        marginRight = margin // right margin, in pixels
+        marginRight = margin, // right margin, in pixels
+        translateX // starting x translation
     }) {
         // set up global chart dimensions, including bounded dimensions
         tileChartDimensions = {
@@ -253,7 +275,7 @@
         tileChartBounds = chartBounds.append("g")
             .attr("id", "tile-chart-bounds")
             .style("transform", `translate(${
-                tileChartDimensions.margin.left
+                translateX
             }px, ${
                 tileChartDimensions.margin.top
             }px)`);
@@ -605,6 +627,7 @@
                     .attr("height", tileChartDimensions.boundedHeight / data.length)
                     .attr("width", tileChartDimensions.boundedWidth - annotationGap)
                     .style("fill", d => tileColorScale(colorAccessor(d)));
+        
         // draw year bands
         tileChartBounds.select(".annotations")
             .append("rect")
@@ -637,8 +660,6 @@
                 .attr("transform", "rotate(-90)")
                 .attr("text-anchor", "middle")
                 .text("2015 accumulation")
-
-        tileChartDrawn.value = true;
     }
 
     function drawBarChart(data) {
@@ -753,22 +774,60 @@
                     .style("fill", d => scatterColorScale(colorAccessor(d)));
     }
 
+    function showChart(hiddenChartBounds, translateX, translateY) {
+        hiddenChartBounds
+            .attr("opacity", 0)
+            .attr("display", "auto")
+            .transition()
+            .duration(transitionLength)
+            .attr("opacity", 1)
+            .style("transform", `translate(${
+                translateX
+            }px, ${
+                translateY
+            }px)`);
+    }
+
+    function hideChart(visibleChartBounds) {
+        visibleChartBounds
+            .transition()
+            .duration(transitionLength)
+            .attr("opacity", 0)
+        setTimeout(function() {
+            visibleChartBounds
+                .attr("display", "none")
+        }, transitionLength)
+    }
+
+    function moveChart(visibleChartBounds, translateX, translateY) {
+        visibleChartBounds
+            .transition()
+            .duration(transitionLength)
+            .style("transform", `translate(${
+                translateX
+            }px, ${
+                translateY
+            }px)`);
+    }
+
     function updateChartView(index) {
+        const barChartHidden = barChartBounds.node().attributes.display.value == 'none';
+        const scatterChartHidden = scatterChartBounds.node().attributes.display.value == 'none';
         if (index == 1) {
-            console.log('1');
+            moveChart(tileChartBounds, tileChartTranslateX1, tileChartDimensions.margin.top)
+            if (!barChartHidden) hideChart(barChartBounds)            
         } else if (index == 2) {
-            tileChartDimensions.margin.left = chartDimensions.boundedWidth / 5
-            tileChartBounds
-                .transition()
-                .duration(2000)
-                .style("transform", `translate(${
-                    tileChartDimensions.margin.left
-                }px, ${
-                    tileChartDimensions.margin.top
-                }px)`);
-            console.log(2)
+            moveChart(tileChartBounds, tileChartTranslateX2, tileChartDimensions.margin.top)
+            if (barChartHidden)  {
+                showChart(barChartBounds, barChartTranslateX2, barChartDimensions.margin.top)
+            } else {
+                moveChart(barChartBounds, barChartTranslateX2, barChartDimensions.margin.top)
+            }
+            if (!scatterChartHidden) hideChart(scatterChartBounds) 
         } else if (index == 3) {
-            console.log(3)
+            moveChart(tileChartBounds, tileChartTranslateX3, tileChartDimensions.margin.top)
+            moveChart(barChartBounds, barChartTranslateX3, barChartDimensions.margin.top)
+            showChart(scatterChartBounds, scatterChartTranslateX3, scatterChartDimensions.margin.top)
         }
     }
 </script>
-- 
GitLab


From 63fde7429085cb8d2b1586fa77fcab1064341ca3 Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Fri, 25 Oct 2024 10:08:32 -0500
Subject: [PATCH 11/38] add bar chart x axis

---
 src/components/WildfireAerosolsViz.vue | 153 ++++++++++++++++---------
 1 file changed, 101 insertions(+), 52 deletions(-)

diff --git a/src/components/WildfireAerosolsViz.vue b/src/components/WildfireAerosolsViz.vue
index 500ae52..b93b522 100644
--- a/src/components/WildfireAerosolsViz.vue
+++ b/src/components/WildfireAerosolsViz.vue
@@ -56,7 +56,7 @@
     const nIndices = 3;
     const chart = ref(null);
     const chartTitle = 'Title of chart';
-    const chartHeight = mobileView ? window.innerHeight * 0.6 : window.innerHeight * 0.8;
+    const chartHeight = mobileView ? window.innerHeight * 0.6 : window.innerHeight * 0.7;
     let chartWidth;
     let chartDimensions;
     let chartBounds;
@@ -66,8 +66,6 @@
     let tileChartDimensions;
     let tileChartBounds;
     let tileColorScale;
-    // let xScale;
-    // let xAxis;
     let yScale;
     let yAxis;
     let barChartTranslateX2;
@@ -77,6 +75,7 @@
     let barXScale;
     let barYScale;
     let barXAxis;
+    const barXAxisPosition = 'top';
     const barColors = {Mannosan: '#000000', Galactosan: '#989898', Levoglucosan: '#c8c8c8'};
     let barColorScale;
     let scatterChartTranslateX3;
@@ -113,14 +112,19 @@
             });
 
             if (tileData.value.length > 0 && barData.value.length > 0) {
+                
                 // initialize chart elements
                 chartWidth = chart.value.offsetWidth;
                 initChart({
                     width: chartWidth,
                     height: chartHeight,
-                    margin: mobileView ? 5 : 10
+                    margin: 0
                 })
 
+                const defaultMargin = mobileView ? 5 : 10;
+                const sharedTopMargin = 50;
+                const sharedBottomMargin = 45;
+
                 const tileChartWidth = chartDimensions.boundedWidth / 3
                 tileChartTranslateX1 = 350;
                 tileChartTranslateX2 = 200;
@@ -128,9 +132,11 @@
                 initTileChart({
                     width: tileChartWidth,
                     height: chartHeight,
-                    margin: mobileView ? 5 : 10,
+                    margin: defaultMargin,
                     marginLeft: mobileView ? 60: 80,
                     marginRight: mobileView ? 60: 40,
+                    marginTop: sharedTopMargin,
+                    marginBottom: sharedBottomMargin,
                     translateX: tileChartTranslateX1
                 });
 
@@ -140,8 +146,10 @@
                 initBarChart({
                     width: barChartWidth,
                     height: chartHeight,
-                    margin: mobileView ? 5 : 10,
+                    margin: defaultMargin,
                     marginLeft: mobileView ? 20 : 80,
+                    marginTop: sharedTopMargin,
+                    marginBottom: sharedBottomMargin,
                     translateX: barChartTranslateX2});
 
                 const scatterChartWidth = chartDimensions.boundedWidth - tileChartWidth - barChartWidth
@@ -149,8 +157,10 @@
                 initScatterChart({
                     width: scatterChartWidth,
                     height: chartHeight,
-                    margin: mobileView ? 5 : 10,
+                    margin: defaultMargin,
                     marginLeft: mobileView ? 20 : 60,
+                    marginTop: sharedTopMargin,
+                    marginBottom: sharedBottomMargin,
                     translateX: scatterChartTranslateX3});
 
                 // draw charts
@@ -334,7 +344,13 @@
         initBarYScale()
 
         // Initialize axes
-        initXAxis({axis: barXAxis, bounds: barChartBounds, chartDims: barChartDimensions})
+        initBarXAxis(
+            {
+                bounds: barChartBounds, 
+                chartDims: barChartDimensions,
+                axisPosition: barXAxisPosition
+            }
+        )
 
         // Add groups for visual elements
         barChartBounds.append("g")
@@ -400,16 +416,16 @@
             .range([0, scatterChartDimensions.boundedWidth]);
     }
 
-    function initXAxis({
-        axis,
+    function initBarXAxis({
         bounds,
-        chartDims
+        chartDims,
+        axisPosition = 'bottom'
     }) {
         // add group for x axis
-        axis = bounds.append("g")
+        barXAxis = bounds.append("g")
             .attr("id", "x-axis")
             .attr("class", "axis")
-            .attr("transform", `translate(0,${chartDims.boundedHeight})`)
+            .attr("transform", `translate(0,${axisPosition === 'bottom' ? chartDims.boundedHeight : 0})`)
             .attr("aria-hidden", true); // hide from screen reader
     }
 
@@ -441,6 +457,7 @@
         titleTextAnchor = "middle",
         titleBaseline = "text-before-edge",
         titleAngle = -90,
+        nticks = null,
         tickSize = 0,
         tickPadding = 5,
         tickType = "numeric",
@@ -453,10 +470,10 @@
         // if numeric ticks, include specification of format
         if (tickType == "numeric" && !customSuffix) {
             axis
-                .call(d3[axisFxn](axisScale).tickSize(tickSize).tickPadding(tickPadding).tickFormat(d3.format(tickFormat)));
+                .call(d3[axisFxn](axisScale).ticks(nticks).tickSize(tickSize).tickPadding(tickPadding).tickFormat(d3.format(tickFormat)));
         } else if (tickType == "numeric" && customSuffix) {
             axis
-                .call(d3[axisFxn](axisScale).tickSize(tickSize).tickPadding(tickPadding).tickFormat(d => d3.format(tickFormat)(d) + ' ' + customSuffix));
+                .call(d3[axisFxn](axisScale).ticks(nticks).tickSize(tickSize).tickPadding(tickPadding).tickFormat(d => d3.format(tickFormat)(d) + ' ' + customSuffix));
         } else {
             axis
                 .call(d3[axisFxn](axisScale).tickSize(tickSize).tickPadding(tickPadding));
@@ -486,40 +503,48 @@
             .text(axisTitle);
     }
 
-    // function drawXAxis({
-    //     axisTitle = '',
-    //     titleX = tileChartDimensions.boundedWidth / 2,
-    //     titleY = tileChartDimensions.margin.bottom,
-    //     titleTextAnchor = "middle",
-    //     titleBaseline = "text-after-edge",
-    //     titleAngle = 0,
-    //     tickSize = 0,
-    //     tickPadding = 5,
-    //     tickType = 'numeric',
-    //     tickFormat = ".2f",
-    //     customSuffix = null,
-    //     textAngle = 0, 
-    //     keepDomain = true,
-    // }) {
-    //     drawAxis({
-    //         axis: xAxis,
-    //         axisScale: xScale,
-    //         axisFxn: 'axisBottom'
-    //     }, {
-    //         axisTitle: axisTitle,
-    //         titleX: titleX,
-    //         titleY: titleY,
-    //         titleTextAnchor: titleTextAnchor,
-    //         titleBaseline: titleBaseline,
-    //         titleAngle: titleAngle,tickSize: tickSize,
-    //         tickPadding: tickPadding,
-    //         tickType: tickType,
-    //         tickFormat: tickFormat,
-    //         customSuffix: customSuffix,
-    //         textAngle: textAngle,
-    //         keepDomain: keepDomain,
-    //     })
-    // }
+    function drawXAxis({
+        axis,
+        axisScale,
+        chartDims
+    }, {
+        axisPosition = 'bottom',
+        axisTitle = '',
+        titleX = chartDims.boundedWidth / 2,
+        titleY = axisPosition === 'bottom' ? chartDims.margin.bottom : -chartDims.margin.top,
+        titleTextAnchor = "middle",
+        titleBaseline = axisPosition === 'bottom' ? "text-after-edge" : "text-before-edge",
+        titleAngle = 0,
+        nticks = null,
+        tickSize = 0,
+        tickPadding = 5,
+        tickType = 'numeric',
+        tickFormat = ".2f",
+        customSuffix = null,
+        textAngle = 0, 
+        keepDomain = true,
+    }) {
+        drawAxis({
+            axis: axis,
+            axisScale: axisScale,
+            axisFxn: axisPosition === 'bottom' ? 'axisBottom' : 'axisTop'
+        }, {
+            axisTitle: axisTitle,
+            titleX: titleX,
+            titleY: titleY,
+            titleTextAnchor: titleTextAnchor,
+            titleBaseline: titleBaseline,
+            titleAngle: titleAngle,
+            nticks: nticks,
+            tickSize: tickSize,
+            tickPadding: tickPadding,
+            tickType: tickType,
+            tickFormat: tickFormat,
+            customSuffix: customSuffix,
+            textAngle: textAngle,
+            keepDomain: keepDomain,
+        })
+    }
 
     function drawYAxis({
         axis,
@@ -532,6 +557,7 @@
         titleTextAnchor = "middle",
         titleBaseline = "text-before-edge",
         titleAngle = -90,
+        nticks = null,
         tickSize = 0,
         tickPadding = 5,
         tickType = 'numeric',
@@ -552,6 +578,7 @@
             titleTextAnchor: titleTextAnchor,
             titleBaseline: titleBaseline,
             titleAngle: titleAngle,
+            nticks: nticks,
             tickSize: tickSize,
             tickPadding: tickPadding,
             tickType: tickType,
@@ -599,8 +626,17 @@
         yScale
             .domain([0, d3.max(data, yAccessor) + 10]);
         drawYAxis(
-            {axis: yAxis, axisScale: yScale, chartDims: tileChartDimensions}, 
-            {tickFormat: ".0f", customSuffix: 'cm', tickSize: 3, keepDomain: false}
+            {
+                axis: yAxis, 
+                axisScale: yScale, 
+                chartDims: tileChartDimensions
+            }, 
+            {
+                tickFormat: ".0f", 
+                customSuffix: 'cm', 
+                tickSize: 3, 
+                keepDomain: false
+            }
         )
 
         ///////////////////////////////////
@@ -695,7 +731,20 @@
         // // set domain for xScale
         barXScale
             .domain([0, d3.max(series, d => d3.max(d, d => d[1]))]);
-        // drawXAxis({axisTitle: 'Climate vulnerability'})
+        drawXAxis(
+            {
+                axis: barXAxis, 
+                axisScale: barXScale, 
+                chartDims: barChartDimensions
+            }, 
+            {
+                axisPosition: barXAxisPosition, 
+                axisTitle: 'Sugar concentration (picogram/mL)', 
+                nticks: 4,
+                tickFormat: '.1s', 
+                tickSize: 3
+            }
+        )
         
         ///////////////////////////////////
         /////    SET UP COLOR SCALE   /////
-- 
GitLab


From 45f23ae6888d48fa603f006ef5612c50d1d1c6df Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Fri, 25 Oct 2024 14:57:55 -0500
Subject: [PATCH 12/38] Add bar chart legend

---
 src/components/WildfireAerosolsViz.vue | 225 +++++++++++++++++++++++--
 1 file changed, 209 insertions(+), 16 deletions(-)

diff --git a/src/components/WildfireAerosolsViz.vue b/src/components/WildfireAerosolsViz.vue
index b93b522..81115b9 100644
--- a/src/components/WildfireAerosolsViz.vue
+++ b/src/components/WildfireAerosolsViz.vue
@@ -76,6 +76,7 @@
     let barYScale;
     let barXAxis;
     const barXAxisPosition = 'top';
+    let barColorCategories;
     const barColors = {Mannosan: '#000000', Galactosan: '#989898', Levoglucosan: '#c8c8c8'};
     let barColorScale;
     let scatterChartTranslateX3;
@@ -122,27 +123,27 @@
                 })
 
                 const defaultMargin = mobileView ? 5 : 10;
-                const sharedTopMargin = 50;
-                const sharedBottomMargin = 45;
+                const sharedTopMargin = mobileView ? 100 : 110;
+                const sharedBottomMargin = mobileView ? 0 : 10;
 
                 const tileChartWidth = chartDimensions.boundedWidth / 3
-                tileChartTranslateX1 = 350;
-                tileChartTranslateX2 = 200;
-                tileChartTranslateX3 = 80;
+                tileChartTranslateX1 = mobileView ? 40 : 350;
+                tileChartTranslateX2 = mobileView ? 40 : 200;
+                tileChartTranslateX3 = mobileView ? 75 : 80;
                 initTileChart({
                     width: tileChartWidth,
                     height: chartHeight,
                     margin: defaultMargin,
-                    marginLeft: mobileView ? 60: 80,
-                    marginRight: mobileView ? 60: 40,
+                    marginLeft: mobileView ? 55: 80,
+                    marginRight: mobileView ? 5: 40,
                     marginTop: sharedTopMargin,
                     marginBottom: sharedBottomMargin,
                     translateX: tileChartTranslateX1
                 });
 
                 const barChartWidth = chartDimensions.boundedWidth / 3
-                barChartTranslateX2 = tileChartWidth + 150;
-                barChartTranslateX3 = tileChartWidth + 80;
+                barChartTranslateX2 = mobileView ? tileChartWidth + 40 : tileChartWidth + 150;
+                barChartTranslateX3 = mobileView ? tileChartWidth + 30 : tileChartWidth + 80;
                 initBarChart({
                     width: barChartWidth,
                     height: chartHeight,
@@ -163,7 +164,7 @@
                     marginBottom: sharedBottomMargin,
                     translateX: scatterChartTranslateX3});
 
-                // draw charts
+                // draw charts, hiding bar and scatter charts to start
                 drawTileChart(tileData.value);
                 drawBarChart(barData.value);
                 barChartBounds
@@ -171,6 +172,9 @@
                 drawScatterChart(scatterData.value);
                 scatterChartBounds
                     .attr("display", "none");
+
+                // add legends
+                addBarLegend()
             } else {
                 console.error('Error loading data');
             }
@@ -452,6 +456,7 @@
         chartDims
     }, {
         axisTitle = '',
+        axisSubtitle = null,
         titleX = -chartDims.boundedHeight / 2,
         titleY = -chartDims.margin.left,
         titleTextAnchor = "middle",
@@ -490,7 +495,7 @@
             .attr("transform", `rotate(${textAngle})`);
 
         // add axis title
-        axis
+        axisTitle = axis
             .append("text")
             .attr("class", "axis-title")
             .attr("x", titleX)
@@ -501,6 +506,20 @@
             .attr("role", "presentation")
             .attr("aria-hidden", true)
             .text(axisTitle);
+
+        if (axisSubtitle) {
+            axisTitle.append("tspan")
+                .attr("class", "axis-subtitle")
+                .attr("x", titleX)
+                .attr("y", titleY)
+                .attr("dy", "1em")
+                .attr("transform", `rotate(${titleAngle})`)
+                .attr("text-anchor", titleTextAnchor)
+                .attr("dominant-baseline", titleBaseline)
+                .attr("role", "presentation")
+                .attr("aria-hidden", true)
+                .text(axisSubtitle);
+        }
     }
 
     function drawXAxis({
@@ -510,6 +529,7 @@
     }, {
         axisPosition = 'bottom',
         axisTitle = '',
+        axisSubtitle = null,
         titleX = chartDims.boundedWidth / 2,
         titleY = axisPosition === 'bottom' ? chartDims.margin.bottom : -chartDims.margin.top,
         titleTextAnchor = "middle",
@@ -530,6 +550,7 @@
             axisFxn: axisPosition === 'bottom' ? 'axisBottom' : 'axisTop'
         }, {
             axisTitle: axisTitle,
+            axisSubtitle: axisSubtitle,
             titleX: titleX,
             titleY: titleY,
             titleTextAnchor: titleTextAnchor,
@@ -552,6 +573,7 @@
         chartDims
     }, {
         axisTitle = '',
+        axisSubtitle = null,
         titleX = -chartDims.boundedHeight / 2,
         titleY = -chartDims.margin.left,
         titleTextAnchor = "middle",
@@ -573,6 +595,7 @@
             chartDims: chartDims
         }, {
             axisTitle: axisTitle,
+            axisSubtitle: axisSubtitle,
             titleX: titleX,
             titleY: titleY,
             titleTextAnchor: titleTextAnchor,
@@ -703,7 +726,7 @@
         /////    SET UP ACCESSOR FUNCTIONS    /////
         ///////////////////////////////////////////
         const yAccessor = d => d.depth_cm;
-        const xAccessor = d => d.picogram_per_mL;
+        const xAccessor = d => d.picogram_per_mL/1000; // convert to nanogram
         const keyAccessor = d => d.sugar;
         const colorAccessor = d => d.sugar;
 
@@ -739,9 +762,10 @@
             }, 
             {
                 axisPosition: barXAxisPosition, 
-                axisTitle: 'Sugar concentration (picogram/mL)', 
+                axisTitle: 'Sugar concentration', 
+                axisSubtitle: 'nanogram/mL',
                 nticks: 4,
-                tickFormat: '.1s', 
+                tickFormat: '.0f', 
                 tickSize: 3
             }
         )
@@ -749,8 +773,8 @@
         ///////////////////////////////////
         /////    SET UP COLOR SCALE   /////
         ///////////////////////////////////
-        const colorCategories = [... new Set(data.map(colorAccessor))];
-        initBarColorScale(colorCategories)
+        barColorCategories = [... new Set(data.map(colorAccessor))];
+        initBarColorScale(barColorCategories)
 
         ////////////////////////////////////
         /////    ADD CHART ELEMENTS    /////
@@ -775,6 +799,112 @@
                 .style("fill", d => barColorScale(d.key))
     }
 
+    function addBarLegend() {
+        const barLegendGroup = barChartBounds.append("g")
+            .attr("id", "bar-chart-legend")
+            .style("transform", `translate(${
+                0
+            }px, ${
+                -barChartDimensions.margin.top / 2
+            }px)`);
+
+        // // Add legend title
+        // barLegendGroup.append("text")
+        //     .text('Sugars')
+        //     .attr("id", "legend-title")
+        //     .attr("class", "axis-title")
+        //     .attr("y", chartDimensions.margin.top / 2)
+        //     .attr("dominant-baseline", "central")
+
+        const legendRectSize = barYScale.bandwidth();
+        const interItemSpacing = mobileView ? 15 : 10;
+        const intraItemSpacing = 6;
+
+        // Append group for each legend entry
+        const legendGroup = barLegendGroup.selectAll(".legend-item")
+            .data(barColorCategories)
+            .enter()
+            .append("g")
+            .attr("class", "legend-item")
+
+        // Add rectangles for each group
+        legendGroup.append("rect")
+            .attr("class", "legend-rect")
+            .attr("x", 0)
+            .attr("y", chartDimensions.margin.top / 2 - legendRectSize / 2.5)
+            .attr("width", legendRectSize)
+            .attr("height", legendRectSize)
+            .style("fill", d => barColorScale(d))
+        
+        // Add text for each group
+        legendGroup.append("text")
+            .attr("class", "legend-text")
+            .attr("x", legendRectSize + intraItemSpacing) // put text to the right of the rectangle
+            .attr("y", chartDimensions.margin.top / 2)
+            .attr("text-anchor", "start") // left-align text
+            .attr("dominant-baseline", "central")
+            .text(d => d);
+
+        // Position legend groups
+        // https://stackoverflow.com/questions/20224611/d3-position-text-element-dependent-on-length-of-element-before
+        const xBuffer = 6; // set xBuffer for use in mobile row x translations
+        legendGroup
+            .attr("transform", (d, i) => {
+            // Compute total width of preceeding legend items, with spacing
+            // Start with width of legend title
+            const titleWidth = 0 //d3.select('#legend-title')._groups[0][0].getBBox().width + interItemSpacing;
+            
+            // Begin right of legend
+            let cumulativeWidth = titleWidth;
+            // if (mobileView) {
+                // On mobile, only use preceding items in same row to find cumulative width
+                // row 1: items 0 and 1
+                if (i < 2) {
+                for (let j = 0; j < i; j++) {
+                    cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
+                }
+                }
+                // row 2: items 2, 3 and 4
+                else if (i < 5) {
+                for (let j = 2; j < i; j++) {
+                    cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
+                }
+                }
+                // row 3: item 5 
+                else if (i === 5) {
+                for (let j = 2; j < i; j++) {
+                    // Actually storing width of row 2 here, to use to set selfSupplyEnd
+                    cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
+                }
+                }
+            // } else {
+                // on desktop, iterate through all preceding items to find cumulative width, since all items in 1 row
+                // for (let j = 0; j < i; j++) {
+                // cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
+                // }
+            // }
+
+            let yTranslation = 0;
+            // Determine x and y translation
+            // set y translation for each row
+            // adjust row starting position for 2nd and third rows by -titleWidth
+            // if (mobileView) {
+                if (i < 2) {
+                    yTranslation = 0;
+                } else if (i < 5) {
+                    yTranslation = mobileView ? legendRectSize * 4 : legendRectSize * 2;
+                    cumulativeWidth = cumulativeWidth - titleWidth;
+                } else {
+                    yTranslation = legendRectSize * 5.75
+                    cumulativeWidth = xBuffer; // for last item just translate by xBuffer
+                } 
+            // }
+
+            // translate each group by that width and height
+            return "translate(" + cumulativeWidth + "," + yTranslation + ")"
+        })
+    }
+
     function drawScatterChart(data) {
         //////////////////////////////
         /////    PROCESS DATA    /////
@@ -879,6 +1009,44 @@
             showChart(scatterChartBounds, scatterChartTranslateX3, scatterChartDimensions.margin.top)
         }
     }
+
+    // // https://gist.github.com/mbostock/7555321
+    // function wrap(text) {
+    //     text.each(function() {
+    //         var text = d3.select(this),
+    //         words = text.text().split(/\s|-+/).reverse(),
+    //         word,
+    //         line = [],
+    //         lineNumber = 0,
+    //         lineHeight = 1.1, // ems
+    //         width = text.attr("text-width"),
+    //         x = text.attr("x"),
+    //         y = text.attr("y"),
+    //         dy = parseFloat(text.attr("dy")),
+    //         dx = parseFloat(text.attr("dx")),
+    //         tspan = text.text(null).append("tspan").attr("y", y).attr("dy", dy + "em");
+
+    //         while ((word = words.pop())) {
+    //         line.push(word);
+    //         tspan.text(line.join(" "));
+    //             if (tspan.node().getComputedTextLength() > width) {
+    //             line.pop();
+    //             tspan.text(line.join(" "));
+    //             line = [word];
+    //             tspan = text.append("tspan").attr("x", x).attr("y", y).attr("dx", dx).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
+    //             }
+    //         }
+
+    //         // https://stackoverflow.com/questions/60558291/wrapping-and-vertically-centering-text-using-d3-js
+    //         if (lineNumber > 0) {
+    //             const startDy = -(lineNumber * (lineHeight / 2));
+    //             text
+    //                 .selectAll("tspan")
+    //                 .attr("dy", (d, i) => startDy + lineHeight * i + "em");
+    //         }
+    //     }
+    // )};
+
 </script>
 
 <style scoped lang="scss">
@@ -975,8 +1143,12 @@
 /* css for elements added/classed w/ d3 */
     .axis-text {
         font-size: 1.6rem;
+        font-weight: 300;
         font-family: var(--default-font);
         user-select: none;
+        @media only screen and (max-width: 600px) {
+            font-size: 1.4rem;
+        }
     }
     .axis-title {
         font-size: 1.8rem;
@@ -984,8 +1156,29 @@
         font-weight: 900;
         fill: var(--color-text);
         user-select: none;
+        @media only screen and (max-width: 600px) {
+            font-size: 1.6rem;
+        }
+    }
+    .axis-subtitle {
+        font-size: 1.8rem;
+        font-family: var(--default-font);
+        font-weight: 300;
+        fill: var(--color-text);
+        user-select: none;
+        @media only screen and (max-width: 600px) {
+            font-size: 1.6rem;
+        }
     }
     .year-bands {
         fill: var(--medium-grey);
     }
+    .legend-text {
+        font-size: 1.6rem;
+        font-family: var(--default-font);
+        user-select: none;
+        @media only screen and (max-width: 600px) {
+            font-size: 1.4rem;
+        }
+    }
 </style>
\ No newline at end of file
-- 
GitLab


From ad558f5b6210e30468f308d39e01d4536aa1a78f Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Fri, 25 Oct 2024 15:50:09 -0500
Subject: [PATCH 13/38] add rough tile legend

---
 src/components/WildfireAerosolsViz.vue | 91 +++++++++++++++++++++-----
 1 file changed, 75 insertions(+), 16 deletions(-)

diff --git a/src/components/WildfireAerosolsViz.vue b/src/components/WildfireAerosolsViz.vue
index 81115b9..a5dd9b7 100644
--- a/src/components/WildfireAerosolsViz.vue
+++ b/src/components/WildfireAerosolsViz.vue
@@ -55,6 +55,7 @@
     const justClicked = ref(false);
     const nIndices = 3;
     const chart = ref(null);
+    let chartSVG;
     const chartTitle = 'Title of chart';
     const chartHeight = mobileView ? window.innerHeight * 0.6 : window.innerHeight * 0.7;
     let chartWidth;
@@ -123,7 +124,7 @@
                 })
 
                 const defaultMargin = mobileView ? 5 : 10;
-                const sharedTopMargin = mobileView ? 100 : 110;
+                const sharedTopMargin = mobileView ? 100 : 120;
                 const sharedBottomMargin = mobileView ? 0 : 10;
 
                 const tileChartWidth = chartDimensions.boundedWidth / 3
@@ -138,7 +139,7 @@
                     marginRight: mobileView ? 5: 40,
                     marginTop: sharedTopMargin,
                     marginBottom: sharedBottomMargin,
-                    translateX: tileChartTranslateX1
+                    translateX: tileChartTranslateX3
                 });
 
                 const barChartWidth = chartDimensions.boundedWidth / 3
@@ -151,7 +152,7 @@
                     marginLeft: mobileView ? 20 : 80,
                     marginTop: sharedTopMargin,
                     marginBottom: sharedBottomMargin,
-                    translateX: barChartTranslateX2});
+                    translateX: barChartTranslateX3});
 
                 const scatterChartWidth = chartDimensions.boundedWidth - tileChartWidth - barChartWidth
                 scatterChartTranslateX3 = tileChartWidth + barChartWidth + 50;
@@ -167,13 +168,14 @@
                 // draw charts, hiding bar and scatter charts to start
                 drawTileChart(tileData.value);
                 drawBarChart(barData.value);
-                barChartBounds
-                    .attr("display", "none");
+                // barChartBounds
+                //     .attr("display", "none");
                 drawScatterChart(scatterData.value);
-                scatterChartBounds
-                    .attr("display", "none");
+                // scatterChartBounds
+                //     .attr("display", "none");
 
                 // add legends
+                addTileLegend()
                 addBarLegend()
             } else {
                 console.error('Error loading data');
@@ -240,7 +242,7 @@
         }
 
         // draw canvas for chart
-        const chartSVG = d3.select("#chart-container")
+        chartSVG = d3.select("#chart-container")
             .append("svg")
                 .attr("viewBox", [0, 0, (chartDimensions.width), (chartDimensions.height)].join(' '))
                 .attr("width", "100%")
@@ -721,6 +723,68 @@
                 .text("2015 accumulation")
     }
 
+    function addTileLegend() {
+        // build list of posible counts (0 to 366)
+        let count_list = [];
+        for (let i = 1; i <= tileColorScale.domain()[1]; i++) {
+            count_list.push(i);
+        }
+
+        // define gradient for legend
+        chartSVG.append("defs")
+            .append("linearGradient")
+            .attr("id", "gradient-particles")
+            .attr("x1", "0%").attr("y1", "0%")
+            .attr("x2", "100%").attr("y2", "0%")
+            .selectAll("stop")
+            .data(count_list)
+                .enter()
+                .append("stop")
+                .attr("offset", (d,i) => i/(count_list.length-1))
+                .attr("stop-color", d => tileColorScale(d))
+
+        const legendGroup = tileChartBounds.append("g")
+            .attr("id", "tile-chart-legend")
+
+        // append legend title
+        legendGroup.append("text")
+              .attr("class", "axis-title")
+              .attr("text-anchor", "start")
+              .attr("x", 0)
+              .attr("y", -tileChartDimensions.margin.top)
+              .attr("dominant-baseline", "text-before-edge")
+              .text("Particulate count")
+
+        // append legend text
+        
+        const xBuffer = 5;
+        legendGroup.append("text")
+              .attr("class", "axis-subtitle")
+              .attr("text-anchor", "end")
+              .attr("dominant-baseline", "central")
+              .attr("x", - xBuffer)
+              .attr("y", -tileChartDimensions.margin.top / 2)
+              .text("low")
+
+        legendGroup.append("text")
+              .attr("class", "axis-subtitle")
+              .attr("text-anchor", "start")
+              .attr("dominant-baseline", "central")
+              .attr("x", tileChartDimensions.boundedWidth / 2 + xBuffer)
+              .attr("y", -tileChartDimensions.margin.top / 2)
+              .text("high")
+        
+        // append legend rectangle
+        const rectHeight = tileChartDimensions.margin.top / 4
+        legendGroup.append("rect")
+              .attr("class", "c1p2 matrixLegend")
+              .attr("width", tileChartDimensions.boundedWidth / 2)
+              .attr("height", rectHeight)
+              .attr("fill", "url(#gradient-particles)")
+              .attr("x", 0)
+              .attr("y", -tileChartDimensions.margin.top / 2 - rectHeight / 2)
+    }
+
     function drawBarChart(data) {
         ///////////////////////////////////////////
         /////    SET UP ACCESSOR FUNCTIONS    /////
@@ -802,18 +866,13 @@
     function addBarLegend() {
         const barLegendGroup = barChartBounds.append("g")
             .attr("id", "bar-chart-legend")
-            .style("transform", `translate(${
-                0
-            }px, ${
-                -barChartDimensions.margin.top / 2
-            }px)`);
 
         // // Add legend title
         // barLegendGroup.append("text")
         //     .text('Sugars')
         //     .attr("id", "legend-title")
         //     .attr("class", "axis-title")
-        //     .attr("y", chartDimensions.margin.top / 2)
+        //     .attr("y", barChartDimensions.margin.top / 2)
         //     .attr("dominant-baseline", "central")
 
         const legendRectSize = barYScale.bandwidth();
@@ -831,7 +890,7 @@
         legendGroup.append("rect")
             .attr("class", "legend-rect")
             .attr("x", 0)
-            .attr("y", chartDimensions.margin.top / 2 - legendRectSize / 2.5)
+            .attr("y", -barChartDimensions.margin.top / 2 - legendRectSize / 2.5)
             .attr("width", legendRectSize)
             .attr("height", legendRectSize)
             .style("fill", d => barColorScale(d))
@@ -840,7 +899,7 @@
         legendGroup.append("text")
             .attr("class", "legend-text")
             .attr("x", legendRectSize + intraItemSpacing) // put text to the right of the rectangle
-            .attr("y", chartDimensions.margin.top / 2)
+            .attr("y", -barChartDimensions.margin.top / 2)
             .attr("text-anchor", "start") // left-align text
             .attr("dominant-baseline", "central")
             .text(d => d);
-- 
GitLab


From 88ddb4c2d8e0a788b56dfafbc6a69fa2158d487a Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Fri, 25 Oct 2024 22:04:41 -0500
Subject: [PATCH 14/38] add rough scatter legend

---
 src/components/WildfireAerosolsViz.vue | 223 +++++++++++++++++++------
 1 file changed, 174 insertions(+), 49 deletions(-)

diff --git a/src/components/WildfireAerosolsViz.vue b/src/components/WildfireAerosolsViz.vue
index a5dd9b7..61555da 100644
--- a/src/components/WildfireAerosolsViz.vue
+++ b/src/components/WildfireAerosolsViz.vue
@@ -84,9 +84,10 @@
     let scatterChartDimensions;
     let scatterChartBounds;
     let scatterXScale;
+    let scatterColorCategories;
     const scatterColors = {grass: '#c49051', hardwood: '#3c475a', softwood: '#729C9D'};
     let scatterColorScale;
-    const transitionLength = 2000;
+    const transitionLength = 20;
 
     const isFirstImage = computed(() => {
         return currentIndex.value === 1;
@@ -139,12 +140,12 @@
                     marginRight: mobileView ? 5: 40,
                     marginTop: sharedTopMargin,
                     marginBottom: sharedBottomMargin,
-                    translateX: tileChartTranslateX3
+                    translateX: tileChartTranslateX1
                 });
 
                 const barChartWidth = chartDimensions.boundedWidth / 3
                 barChartTranslateX2 = mobileView ? tileChartWidth + 40 : tileChartWidth + 150;
-                barChartTranslateX3 = mobileView ? tileChartWidth + 30 : tileChartWidth + 80;
+                barChartTranslateX3 = mobileView ? tileChartWidth + 10 : tileChartWidth + 80;
                 initBarChart({
                     width: barChartWidth,
                     height: chartHeight,
@@ -152,15 +153,16 @@
                     marginLeft: mobileView ? 20 : 80,
                     marginTop: sharedTopMargin,
                     marginBottom: sharedBottomMargin,
-                    translateX: barChartTranslateX3});
+                    translateX: barChartTranslateX2});
 
-                const scatterChartWidth = chartDimensions.boundedWidth - tileChartWidth - barChartWidth
-                scatterChartTranslateX3 = tileChartWidth + barChartWidth + 50;
+                const scatterChartWidth = chartDimensions.boundedWidth / 3
+                scatterChartTranslateX3 = tileChartWidth + barChartWidth + 80;
                 initScatterChart({
                     width: scatterChartWidth,
                     height: chartHeight,
                     margin: defaultMargin,
-                    marginLeft: mobileView ? 20 : 60,
+                    marginLeft: mobileView ? 20 : scatterChartWidth / 3,
+                    marginRight: scatterChartWidth / 3,
                     marginTop: sharedTopMargin,
                     marginBottom: sharedBottomMargin,
                     translateX: scatterChartTranslateX3});
@@ -168,15 +170,16 @@
                 // draw charts, hiding bar and scatter charts to start
                 drawTileChart(tileData.value);
                 drawBarChart(barData.value);
-                // barChartBounds
-                //     .attr("display", "none");
+                barChartBounds
+                    .attr("display", "none");
                 drawScatterChart(scatterData.value);
-                // scatterChartBounds
-                //     .attr("display", "none");
+                scatterChartBounds
+                    .attr("display", "none");
 
                 // add legends
                 addTileLegend()
                 addBarLegend()
+                addScatterLegend()
             } else {
                 console.error('Error loading data');
             }
@@ -992,8 +995,8 @@
         ///////////////////////////////////
         /////    SET UP COLOR SCALE   /////
         ///////////////////////////////////
-        const colorCategories = [... new Set(data.map(colorAccessor))];
-        initScatterColorScale(colorCategories)
+        scatterColorCategories = [... new Set(data.map(colorAccessor))];
+        initScatterColorScale(scatterColorCategories)
 
         ////////////////////////////////////
         /////    ADD CHART ELEMENTS    /////
@@ -1012,6 +1015,116 @@
                     .style("fill", d => scatterColorScale(colorAccessor(d)));
     }
 
+    function addScatterLegend() {
+        const scatterLegendGroup = scatterChartBounds.append("g")
+            .attr("id", "bar-chart-legend")
+
+        // Add legend title
+        scatterLegendGroup.append("text")
+            .attr("id", "legend-title")
+            .attr("class", "axis-title")
+            .attr("x", scatterChartDimensions.boundedWidth / 2)
+            .attr("y", -scatterChartDimensions.margin.top)
+            .attr("text-anchor", "middle")
+            .attr("dominant-baseline", "text-before-edge")
+            .attr("text-width", scatterChartDimensions.boundedWidth + (scatterChartDimensions.margin.left + scatterChartDimensions.margin.right) / 2)
+            .text('Burned vegetation type')
+            .call(d => wrap(d))
+
+        const legendPointSize = 4;
+        const interItemSpacing = mobileView ? 15 : 10;
+        const intraItemSpacing = 6;
+
+        // Append group for each legend entry
+        const legendGroup = scatterLegendGroup
+            .append("g")
+            .style("transform", `translate(${
+                -scatterChartDimensions.margin.left / 4
+            }px, 0px)`);
+
+        const legendGroups = legendGroup.selectAll(".legend-item")
+            .data(scatterColorCategories)
+            .enter()
+            .append("g")
+            .attr("class", "legend-item")
+
+        // Add rectangles for each group
+        legendGroups.append("circle")
+            .attr("class", "legend-point")
+            .attr("cx", 0)
+            .attr("cy", -scatterChartDimensions.margin.top / 2 - legendPointSize / 2.5)
+            .attr("r", legendPointSize)
+            .style("fill", d => scatterColorScale(d))
+        
+        // Add text for each group
+        legendGroups.append("text")
+            .attr("class", "legend-text")
+            .attr("x", legendPointSize + intraItemSpacing) // put text to the right of the rectangle
+            .attr("y", -scatterChartDimensions.margin.top / 2)
+            .attr("text-anchor", "start") // left-align text
+            .attr("dominant-baseline", "central")
+            .text(d => d);
+
+        // Position legend groups
+        // https://stackoverflow.com/questions/20224611/d3-position-text-element-dependent-on-length-of-element-before
+        const xBuffer = 6; // set xBuffer for use in mobile row x translations
+        legendGroups
+            .attr("transform", (d, i) => {
+                // Compute total width of preceeding legend items, with spacing
+                // Start with width of legend title
+                const titleWidth = 0 //d3.select('#legend-title')._groups[0][0].getBBox().width + interItemSpacing;
+                
+                // Begin right of legend
+                let cumulativeWidth = titleWidth;
+                // if (mobileView) {
+                    // On mobile, only use preceding items in same row to find cumulative width
+                    // row 1: items 0 and 1
+                    if (i < 2) {
+                    for (let j = 0; j < i; j++) {
+                        cumulativeWidth = cumulativeWidth + legendGroups._groups[0][j].getBBox().width + interItemSpacing;
+                    }
+                    }
+                    // row 2: items 2, 3 and 4
+                    else if (i < 5) {
+                    for (let j = 2; j < i; j++) {
+                        cumulativeWidth = cumulativeWidth + legendGroups._groups[0][j].getBBox().width + interItemSpacing;
+                    }
+                    }
+                    // row 3: item 5 
+                    else if (i === 5) {
+                    for (let j = 2; j < i; j++) {
+                        // Actually storing width of row 2 here, to use to set selfSupplyEnd
+                        cumulativeWidth = cumulativeWidth + legendGroups._groups[0][j].getBBox().width + interItemSpacing;
+                    }
+                    }
+                // } else {
+                    // on desktop, iterate through all preceding items to find cumulative width, since all items in 1 row
+                    // for (let j = 0; j < i; j++) {
+                    // cumulativeWidth = cumulativeWidth + legendGroups._groups[0][j].getBBox().width + interItemSpacing;
+                    // }
+                // }
+
+                let yTranslation = 0;
+                // Determine x and y translation
+                // set y translation for each row
+                // adjust row starting position for 2nd and third rows by -titleWidth
+                // if (mobileView) {
+                    if (i < 2) {
+                        yTranslation = 0;
+                    } else if (i < 5) {
+                        yTranslation = mobileView ? barYScale.bandwidth() * 4 : barYScale.bandwidth() * 2;
+                        cumulativeWidth = cumulativeWidth - titleWidth;
+                    } else {
+                        yTranslation = legendPointSize * 5.75
+                        cumulativeWidth = xBuffer; // for last item just translate by xBuffer
+                    } 
+                // }
+
+                // translate each group by that width and height
+                return "translate(" + cumulativeWidth + "," + yTranslation + ")"
+            })
+    }
+
     function showChart(hiddenChartBounds, translateX, translateY) {
         hiddenChartBounds
             .attr("opacity", 0)
@@ -1069,42 +1182,54 @@
         }
     }
 
-    // // https://gist.github.com/mbostock/7555321
-    // function wrap(text) {
-    //     text.each(function() {
-    //         var text = d3.select(this),
-    //         words = text.text().split(/\s|-+/).reverse(),
-    //         word,
-    //         line = [],
-    //         lineNumber = 0,
-    //         lineHeight = 1.1, // ems
-    //         width = text.attr("text-width"),
-    //         x = text.attr("x"),
-    //         y = text.attr("y"),
-    //         dy = parseFloat(text.attr("dy")),
-    //         dx = parseFloat(text.attr("dx")),
-    //         tspan = text.text(null).append("tspan").attr("y", y).attr("dy", dy + "em");
-
-    //         while ((word = words.pop())) {
-    //         line.push(word);
-    //         tspan.text(line.join(" "));
-    //             if (tspan.node().getComputedTextLength() > width) {
-    //             line.pop();
-    //             tspan.text(line.join(" "));
-    //             line = [word];
-    //             tspan = text.append("tspan").attr("x", x).attr("y", y).attr("dx", dx).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
-    //             }
-    //         }
-
-    //         // https://stackoverflow.com/questions/60558291/wrapping-and-vertically-centering-text-using-d3-js
-    //         if (lineNumber > 0) {
-    //             const startDy = -(lineNumber * (lineHeight / 2));
-    //             text
-    //                 .selectAll("tspan")
-    //                 .attr("dy", (d, i) => startDy + lineHeight * i + "em");
-    //         }
-    //     }
-    // )};
+    // https://gist.github.com/mbostock/7555321
+    function wrap(text) {
+        text.each(function() {
+            var text = d3.select(this),
+            words = text.text().split(/\s|-+/).reverse(),
+            word,
+            line = [],
+            lineNumber = 0,
+            lineHeight = 1.1, // ems
+            width = text.attr("text-width"),
+            baseline = text.attr("dominant-baseline"),
+            x = text.attr("x"),
+            y = text.attr("y"),
+            dy = parseFloat(text.attr("dy")),
+            dx = parseFloat(text.attr("dx")),
+            tspan = text.text(null).append("tspan").attr("y", y).attr("dy", dy + "em");
+
+            while ((word = words.pop())) {
+            line.push(word);
+            tspan.text(line.join(" "));
+                if (tspan.node().getComputedTextLength() > width) {
+                line.pop();
+                tspan.text(line.join(" "));
+                line = [word];
+                tspan = text.append("tspan").attr("x", x).attr("y", y).attr("dx", dx).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
+                }
+            }
+
+            // https://stackoverflow.com/questions/60558291/wrapping-and-vertically-centering-text-using-d3-js
+            if (lineNumber > 0) {
+                let lineHeightFactor;
+                switch(baseline) {
+                    case 'central':
+                        lineHeightFactor = 0;
+                        break;
+                    case 'text-before-edge':
+                        lineHeightFactor = 0;
+                        break;
+                    default:
+                        lineHeightFactor = 0;
+                }
+                const startDy = -(lineNumber * (lineHeight / 2)) * lineHeightFactor;
+                text
+                    .selectAll("tspan")
+                    .attr("dy", (d, i) => startDy + lineHeight * i + "em");
+            }
+        }
+    )};
 
 </script>
 
-- 
GitLab


From 2909f5f35a7b380c93787c0608f1349652d76b37 Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Fri, 25 Oct 2024 22:08:34 -0500
Subject: [PATCH 15/38] clean up tile chart legend

---
 src/components/WildfireAerosolsViz.vue | 30 ++++++++++++++------------
 1 file changed, 16 insertions(+), 14 deletions(-)

diff --git a/src/components/WildfireAerosolsViz.vue b/src/components/WildfireAerosolsViz.vue
index 61555da..e515b98 100644
--- a/src/components/WildfireAerosolsViz.vue
+++ b/src/components/WildfireAerosolsViz.vue
@@ -752,20 +752,31 @@
         // append legend title
         legendGroup.append("text")
               .attr("class", "axis-title")
-              .attr("text-anchor", "start")
-              .attr("x", 0)
+              .attr("x", tileChartDimensions.boundedWidth / 2)
               .attr("y", -tileChartDimensions.margin.top)
+              .attr("text-anchor", "middle")
               .attr("dominant-baseline", "text-before-edge")
               .text("Particulate count")
 
+        // append legend rectangle
+        const rectWidth = tileChartDimensions.boundedWidth / 2;
+        const rectHeight = tileChartDimensions.margin.top / 4;
+        const rectX = tileChartDimensions.boundedWidth / 2 - rectWidth / 2;
+        legendGroup.append("rect")
+              .attr("class", "c1p2 matrixLegend")
+              .attr("width", rectWidth)
+              .attr("height", rectHeight)
+              .attr("fill", "url(#gradient-particles)")
+              .attr("x", rectX)
+              .attr("y", -tileChartDimensions.margin.top / 2 - rectHeight / 2)
+
         // append legend text
-        
         const xBuffer = 5;
         legendGroup.append("text")
               .attr("class", "axis-subtitle")
               .attr("text-anchor", "end")
               .attr("dominant-baseline", "central")
-              .attr("x", - xBuffer)
+              .attr("x", rectX - xBuffer)
               .attr("y", -tileChartDimensions.margin.top / 2)
               .text("low")
 
@@ -773,19 +784,10 @@
               .attr("class", "axis-subtitle")
               .attr("text-anchor", "start")
               .attr("dominant-baseline", "central")
-              .attr("x", tileChartDimensions.boundedWidth / 2 + xBuffer)
+              .attr("x", rectX + rectWidth + xBuffer)
               .attr("y", -tileChartDimensions.margin.top / 2)
               .text("high")
         
-        // append legend rectangle
-        const rectHeight = tileChartDimensions.margin.top / 4
-        legendGroup.append("rect")
-              .attr("class", "c1p2 matrixLegend")
-              .attr("width", tileChartDimensions.boundedWidth / 2)
-              .attr("height", rectHeight)
-              .attr("fill", "url(#gradient-particles)")
-              .attr("x", 0)
-              .attr("y", -tileChartDimensions.margin.top / 2 - rectHeight / 2)
     }
 
     function drawBarChart(data) {
-- 
GitLab


From d2f00eb766c9bd7009f5f5224232c602048c1aa3 Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Fri, 25 Oct 2024 22:56:22 -0500
Subject: [PATCH 16/38] extend tick marks and add rectangle to hide

---
 src/components/WildfireAerosolsViz.vue | 47 ++++++++++++++++++++++++--
 1 file changed, 44 insertions(+), 3 deletions(-)

diff --git a/src/components/WildfireAerosolsViz.vue b/src/components/WildfireAerosolsViz.vue
index e515b98..067baad 100644
--- a/src/components/WildfireAerosolsViz.vue
+++ b/src/components/WildfireAerosolsViz.vue
@@ -45,6 +45,8 @@
     // global variables
     const publicPath = import.meta.env.BASE_URL;
     const mobileView = isMobile;
+    const bodyCSS = window.getComputedStyle(document.body)
+    const bkgdColor = bodyCSS.getPropertyValue('--color-background');
     const tileDataFile = 'fii_core4particulates.csv';
     const barDataFile = 'fii_core4sugars.csv';
     const scatterDataFile = 'fii_core4biomass.csv';
@@ -61,6 +63,7 @@
     let chartWidth;
     let chartDimensions;
     let chartBounds;
+    let maskingRect;
     let tileChartTranslateX1;
     let tileChartTranslateX2;
     let tileChartTranslateX3;
@@ -87,7 +90,7 @@
     let scatterColorCategories;
     const scatterColors = {grass: '#c49051', hardwood: '#3c475a', softwood: '#729C9D'};
     let scatterColorScale;
-    const transitionLength = 20;
+    const transitionLength = 1000;
 
     const isFirstImage = computed(() => {
         return currentIndex.value === 1;
@@ -145,7 +148,7 @@
 
                 const barChartWidth = chartDimensions.boundedWidth / 3
                 barChartTranslateX2 = mobileView ? tileChartWidth + 40 : tileChartWidth + 150;
-                barChartTranslateX3 = mobileView ? tileChartWidth + 10 : tileChartWidth + 80;
+                barChartTranslateX3 = mobileView ? tileChartWidth + 10 : tileChartWidth + 50;
                 initBarChart({
                     width: barChartWidth,
                     height: chartHeight,
@@ -172,6 +175,17 @@
                 drawBarChart(barData.value);
                 barChartBounds
                     .attr("display", "none");
+                maskingRect = chartSVG
+                    .append("rect")
+                    .attr("id", "masking-rect")
+                    .attr("x", tileChartTranslateX1 + tileChartDimensions.width)
+                    .attr("y", 0)
+                    .attr("width", barChartDimensions.width + scatterChartDimensions.width)
+                    .attr("height", chartHeight)
+                    .attr("fill", bkgdColor)
+                    .style("transform", `translate(${
+                        -100
+                    }px, 0px)`);
                 drawScatterChart(scatterData.value);
                 scatterChartBounds
                     .attr("display", "none");
@@ -662,10 +676,12 @@
             {
                 tickFormat: ".0f", 
                 customSuffix: 'cm', 
-                tickSize: 3, 
+                tickSize: -chartDimensions.boundedWidth, 
                 keepDomain: false
             }
         )
+        tileChartBounds.selectAll(".tick line")
+            .attr("class", "tick-line")
 
         ///////////////////////////////////
         /////    SET UP COLOR SCALE   /////
@@ -1027,6 +1043,8 @@
             .attr("class", "axis-title")
             .attr("x", scatterChartDimensions.boundedWidth / 2)
             .attr("y", -scatterChartDimensions.margin.top)
+            .attr("dx", 0)
+            .attr("dy", 0)
             .attr("text-anchor", "middle")
             .attr("dominant-baseline", "text-before-edge")
             .attr("text-width", scatterChartDimensions.boundedWidth + (scatterChartDimensions.margin.left + scatterChartDimensions.margin.right) / 2)
@@ -1167,9 +1185,25 @@
         const barChartHidden = barChartBounds.node().attributes.display.value == 'none';
         const scatterChartHidden = scatterChartBounds.node().attributes.display.value == 'none';
         if (index == 1) {
+            maskingRect
+                .transition()
+                .duration(transitionLength)
+                .delay(transitionLength / 4)
+                .style("transform", `translate(${
+                    -100
+                }px, 0px)`);
             moveChart(tileChartBounds, tileChartTranslateX1, tileChartDimensions.margin.top)
             if (!barChartHidden) hideChart(barChartBounds)            
         } else if (index == 2) {
+            console.log(barChartDimensions.width)
+            maskingRect
+                .style("transform", `translate(${
+                    0
+                }px, 0px)`)
+                .transition()
+                .duration(transitionLength)
+                .delay(transitionLength / 4)
+                .style("opacity", "1");
             moveChart(tileChartBounds, tileChartTranslateX2, tileChartDimensions.margin.top)
             if (barChartHidden)  {
                 showChart(barChartBounds, barChartTranslateX2, barChartDimensions.margin.top)
@@ -1178,6 +1212,8 @@
             }
             if (!scatterChartHidden) hideChart(scatterChartBounds) 
         } else if (index == 3) {
+            maskingRect
+                .style("opacity", "0")
             moveChart(tileChartBounds, tileChartTranslateX3, tileChartDimensions.margin.top)
             moveChart(barChartBounds, barChartTranslateX3, barChartDimensions.margin.top)
             showChart(scatterChartBounds, scatterChartTranslateX3, scatterChartDimensions.margin.top)
@@ -1367,4 +1403,9 @@
             font-size: 1.4rem;
         }
     }
+    .tick-line {
+        stroke: var(--dark-grey);
+        stroke-width: 0.5;
+        stroke-dasharray: 1px, 2px;
+    }
 </style>
\ No newline at end of file
-- 
GitLab


From 062bb55822e3adcc910a7c185a169e4ccb19eec2 Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Fri, 25 Oct 2024 23:23:02 -0500
Subject: [PATCH 17/38] update text

---
 src/assets/css/base.css                | 1 +
 src/assets/text/text.js                | 6 +++---
 src/components/WildfireAerosolsViz.vue | 2 +-
 3 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/src/assets/css/base.css b/src/assets/css/base.css
index c2795f5..3551229 100644
--- a/src/assets/css/base.css
+++ b/src/assets/css/base.css
@@ -10,6 +10,7 @@
   --black-soft: #151515;
   --dodger-blue: #1e90ff;
   --medium-grey: #B5B5B5;
+  --dark-grey: #5a5a5a;
   --medium-purple: #8F00DB;
   --medium-blue: #4365A8;
   --medium-orange: #E48951;
diff --git a/src/assets/text/text.js b/src/assets/text/text.js
index 26f586f..4c8501e 100644
--- a/src/assets/text/text.js
+++ b/src/assets/text/text.js
@@ -177,9 +177,9 @@ export default {
             paragraph1: 'Explore total recreational harvest for the five families of inland fish with the largest recreational harvests: <span class="scientificName">Cyprinidae</span> (minnows and carps), <span class="scientificName">Percidae</span> (perch), <span class="scientificName">Salmonidae</span> (salmon, trout, grayling, and whitefish), <span class="scientificName">Bagridae</span> (bagrid catfish), and <span class="scientificName">Centrarchidae</span> (sunfishes). Total recreational harvest is broken out by family, by species, and by country.  Hover over the chart to see the harvest totals, in kilograms'
         },
         WildfireAerosols: {
-            paragraph1: "In the cores, we see that there is more than just ice. Particles from the air, called aerosols, have been deposited in the ice. These aerosols can come from dust, fossil fuel combustion, or wildfires.",
-            paragraph2: "What are the particles made of? These L, M, and G sugars indicate that some of the particules have been deposited by wildfires.",   
-            paragraph3: "Some L/(M+G) peaks indicate that hardwoods were burned, which suggests a further source. Based on hardwood forest locations, one likely source is East Asia."
+            paragraph1: "Each layer of the <a href='/visualizations/climate-charts/#/fire-in-ice/glacier-scan' target='_blank'>collected ice core</a> contains more than just ice. Particles from the air, called aerosols, that were deposited on the surface of the glacier are preserved in the ice. These aerosols can come from dust, fossil fuel combustion, or wildfires.",
+            paragraph2: "Can we tell if any of these particles came from wildfires? Three sugars — levoglucosan, mannosan, and galactosan — are only produced when vegetation burns at high temperatures. These sugars are present throughout the core, which tells us that some of the particles in the ice were deposited by wildfires.",   
+            paragraph3: "The ratio of levoglucosan to the sum of mannosan and galactosan can be used to distinguish between types of vegetation that burned. Alaska is dominated by softwoods, and <a href='/visualizations/climate-charts/#/fire-in-ice/aerosol-paths' target='_blank'>regional fires</a> likely deposit aerosols on the Juneau Ice Field that are captured in the core. But there are also markers of hardwood combustion, which suggests that aerosols are transported to the ice field from wildfires in upwind areas with hardwood forests like East Asia."
         },
         ThreatSankey: {
             paragraph1: 'Land use change is the biggest threat to inland fisheries.'
diff --git a/src/components/WildfireAerosolsViz.vue b/src/components/WildfireAerosolsViz.vue
index 067baad..3a63f03 100644
--- a/src/components/WildfireAerosolsViz.vue
+++ b/src/components/WildfireAerosolsViz.vue
@@ -1297,7 +1297,7 @@
     }
     #aerosol-text-container {
         grid-area: text;
-        height: 10vh;
+        height: 15vh;
         @media screen and (max-height: 770px) {
             height: 30vh;
         }
-- 
GitLab


From 2b5926c15411fd72f6623e7f36af89b84765a217 Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Fri, 25 Oct 2024 23:23:53 -0500
Subject: [PATCH 18/38] fix typo in link

---
 src/assets/text/text.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/assets/text/text.js b/src/assets/text/text.js
index 84dc7c5..b792176 100644
--- a/src/assets/text/text.js
+++ b/src/assets/text/text.js
@@ -161,7 +161,7 @@ export default {
             paragraph2: "Here, we can see the full record of microfossil species assemblages in the Beaufort Sea over the past 2000 years, as captured in the sediment record. The abundance of key indicator species has shifted over time, with recent increases in the relative abundance of <span class='highlight central scientificName' id='kotorachythere'> Kotorachythere </span> and <span class='highlight central scientificName' id='spiroplectimmina'> Spiroplectimmina </span> and decreases in the relative abundance of <span class='highlight scientificName' id='elphidium'> Elphidium </span>, <span class='highlight scientificName' id='cassidulina'> Cassidulina </span>, and <span class='highlight scientificName' id='paracyprideis'> Paracyprideis </span>.",
             paragraph3: "Each bubble in the chart above is scaled to represent the relative abundance of an individual species of microfossil, including the indicator species in the genera <span class='highlight scientificName' id='elphidium'> Elphidium </span>, <span class='highlight scientificName' id='cassidulina'> Cassidulina </span>, <span class='highlight scientificName' id='paracyprideis'> Paracyprideis </span>, <span class='highlight central scientificName' id='kotorachythere'> Kotorachythere </span> and <span class='highlight scientificName' id='spiroplectimmina'> Spiroplectimmina </span>, alongside <span class='highlight central scientificName' id='other-species'> other species </span> of ostracodes and forams. Paired with the bubble chart is a timeline and bar chart displaying the assemblage of species for every 100 years from 0 A.D. to 2000 A.D. Hover over the bar chart to see the relative abundance of the different species within each 100-year window.",
             heading2: "How was this record reconstructed?",
-            paragraph4: "USGS scientists <a href='/visualizations/climate-charts/#/beaufort-sea/beaufort-sea-ice-coring' target='_blank'>collected sediment cores</a> from the ocean floor at the Beaufort Sea continental shelf north of Yukon, Canada. From the sediment cores, they took 1-cm slices that each represent ~5 years of time to build a 2000-year history. The species of microfossils in each core sample, including <a href='/visualizations/climate-charts/#/beaufort-sea/beaufort-sea-species' target='_blank'>forams and ostracodes</a>, along with other biochemical signatures, help the researchers understand past and present climate conditions.",
+            paragraph4: "USGS scientists <a href='/visualizations/climate-charts/#/beaufort-sea/beaufort-sea-sediment-coring' target='_blank'>collected sediment cores</a> from the ocean floor at the Beaufort Sea continental shelf north of Yukon, Canada. From the sediment cores, they took 1-cm slices that each represent ~5 years of time to build a 2000-year history. The species of microfossils in each core sample, including <a href='/visualizations/climate-charts/#/beaufort-sea/beaufort-sea-species' target='_blank'>forams and ostracodes</a>, along with other biochemical signatures, help the researchers understand past and present climate conditions.",
         },
         FishAsFoodCirclePacking: {
             paragraph1: "Explore the global economic value of recreationally-fished inland fish species, in U.S. dollars. Click on the circles in the diagram to see the economic value of species within each fish family, and click on the nested circles to see the economic value of each species in the various countries where it is recreationally fished.",
-- 
GitLab


From fd02553abee8723cd87810a17512ce6a8b04473b Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Fri, 25 Oct 2024 23:29:23 -0500
Subject: [PATCH 19/38] add data notation

---
 src/components/WildfireAerosolsViz.vue | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/src/components/WildfireAerosolsViz.vue b/src/components/WildfireAerosolsViz.vue
index 3a63f03..013d60f 100644
--- a/src/components/WildfireAerosolsViz.vue
+++ b/src/components/WildfireAerosolsViz.vue
@@ -727,6 +727,7 @@
 
         tileChartBounds.select(".annotations")
             .append("text")
+                .attr("class", "axis-text")
                 .attr("x", - yScale(372) / 2)
                 .attr("y", annotationGap / 2)
                 .attr("transform", "rotate(-90)")
@@ -735,6 +736,7 @@
         
         tileChartBounds.select(".annotations")
             .append("text")
+                .attr("class", "axis-text")
                 .attr("x", - yScale(378) - ((tileChartDimensions.boundedHeight - yScale(378)) / 2))
                 .attr("y", annotationGap / 2)
                 .attr("transform", "rotate(-90)")
@@ -882,6 +884,13 @@
                 .attr("height", barYScale.bandwidth())
                 .attr("width", d => barXScale(d[1]) - barXScale(d[0]))
                 .style("fill", d => barColorScale(d.key))
+
+        barChartBounds.append("text")
+            .attr("class", "data-notation")
+            .attr("x", 0)
+            .attr("y", barYScale('400') + barYScale.bandwidth() / 2)
+            .attr("dominant-baseline", "central")
+            .text("missing data")
     }
 
     function addBarLegend() {
@@ -1408,4 +1417,12 @@
         stroke-width: 0.5;
         stroke-dasharray: 1px, 2px;
     }
+    .data-notation {
+        font-size: 1rem;
+        font-family: var(--default-font);
+        user-select: none;
+        @media only screen and (max-width: 600px) {
+            font-size: 0.8rem;
+        }
+    }
 </style>
\ No newline at end of file
-- 
GitLab


From 09f0a2d389f21136a88d47d48f5b418a46af1fda Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Fri, 25 Oct 2024 23:35:57 -0500
Subject: [PATCH 20/38] start cleaning up mobile - more to do

---
 src/components/WildfireAerosolsViz.vue | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/src/components/WildfireAerosolsViz.vue b/src/components/WildfireAerosolsViz.vue
index 013d60f..b9e0505 100644
--- a/src/components/WildfireAerosolsViz.vue
+++ b/src/components/WildfireAerosolsViz.vue
@@ -132,9 +132,9 @@
                 const sharedBottomMargin = mobileView ? 0 : 10;
 
                 const tileChartWidth = chartDimensions.boundedWidth / 3
-                tileChartTranslateX1 = mobileView ? 40 : 350;
-                tileChartTranslateX2 = mobileView ? 40 : 200;
-                tileChartTranslateX3 = mobileView ? 75 : 80;
+                tileChartTranslateX1 = mobileView ? 140 : 350;
+                tileChartTranslateX2 = mobileView ? 65 : 200;
+                tileChartTranslateX3 = mobileView ? 60 : 80;
                 initTileChart({
                     width: tileChartWidth,
                     height: chartHeight,
@@ -147,8 +147,8 @@
                 });
 
                 const barChartWidth = chartDimensions.boundedWidth / 3
-                barChartTranslateX2 = mobileView ? tileChartWidth + 40 : tileChartWidth + 150;
-                barChartTranslateX3 = mobileView ? tileChartWidth + 10 : tileChartWidth + 50;
+                barChartTranslateX2 = mobileView ? tileChartWidth + 55 : tileChartWidth + 150;
+                barChartTranslateX3 = mobileView ? tileChartWidth + 30 : tileChartWidth + 50;
                 initBarChart({
                     width: barChartWidth,
                     height: chartHeight,
@@ -159,7 +159,7 @@
                     translateX: barChartTranslateX2});
 
                 const scatterChartWidth = chartDimensions.boundedWidth / 3
-                scatterChartTranslateX3 = tileChartWidth + barChartWidth + 80;
+                scatterChartTranslateX3 = mobileView ? tileChartWidth + barChartWidth + 50 : tileChartWidth + barChartWidth + 80;
                 initScatterChart({
                     width: scatterChartWidth,
                     height: chartHeight,
@@ -184,7 +184,7 @@
                     .attr("height", chartHeight)
                     .attr("fill", bkgdColor)
                     .style("transform", `translate(${
-                        -100
+                        mobileView ? -30 : -100
                     }px, 0px)`);
                 drawScatterChart(scatterData.value);
                 scatterChartBounds
@@ -1038,7 +1038,7 @@
                     .attr("id", d => 'point-' + identifierAccessor(d))
                     .attr("cx", d => scatterXScale(xAccessor(d)))
                     .attr("cy", d => barYScale(yAccessor(d)) + barYScale.bandwidth()/2)
-                    .attr("r", 4)
+                    .attr("r", mobileView ? 2 : 4)
                     .style("fill", d => scatterColorScale(colorAccessor(d)));
     }
 
@@ -1060,7 +1060,7 @@
             .text('Burned vegetation type')
             .call(d => wrap(d))
 
-        const legendPointSize = 4;
+        const legendPointSize = mobileView ? 2 : 4;
         const interItemSpacing = mobileView ? 15 : 10;
         const intraItemSpacing = 6;
 
@@ -1199,7 +1199,7 @@
                 .duration(transitionLength)
                 .delay(transitionLength / 4)
                 .style("transform", `translate(${
-                    -100
+                    mobileView ? -30 : -100
                 }px, 0px)`);
             moveChart(tileChartBounds, tileChartTranslateX1, tileChartDimensions.margin.top)
             if (!barChartHidden) hideChart(barChartBounds)            
@@ -1207,7 +1207,7 @@
             console.log(barChartDimensions.width)
             maskingRect
                 .style("transform", `translate(${
-                    0
+                    mobileView ? barChartDimensions.width : 0
                 }px, 0px)`)
                 .transition()
                 .duration(transitionLength)
-- 
GitLab


From bcce200f49d7951d78aca0fb94b500a02189c54e Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Sun, 27 Oct 2024 20:55:31 -0500
Subject: [PATCH 21/38] clean up mobile legend a bit

---
 src/assets/text/text.js                |   2 +-
 src/components/WildfireAerosolsViz.vue | 140 +++++++++++++++----------
 2 files changed, 84 insertions(+), 58 deletions(-)

diff --git a/src/assets/text/text.js b/src/assets/text/text.js
index b792176..75c80f6 100644
--- a/src/assets/text/text.js
+++ b/src/assets/text/text.js
@@ -179,7 +179,7 @@ export default {
         WildfireAerosols: {
             paragraph1: "Each layer of the <a href='/visualizations/climate-charts/#/fire-in-ice/glacier-scan' target='_blank'>collected ice core</a> contains more than just ice. Particles from the air, called aerosols, that were deposited on the surface of the glacier are preserved in the ice. These aerosols can come from dust, fossil fuel combustion, or wildfires.",
             paragraph2: "Can we tell if any of these particles came from wildfires? Three sugars — levoglucosan, mannosan, and galactosan — are only produced when vegetation burns at high temperatures. These sugars are present throughout the core, which tells us that some of the particles in the ice were deposited by wildfires.",   
-            paragraph3: "The ratio of levoglucosan to the sum of mannosan and galactosan can be used to distinguish between types of vegetation that burned. Alaska is dominated by softwoods, and <a href='/visualizations/climate-charts/#/fire-in-ice/aerosol-paths' target='_blank'>regional fires</a> likely deposit aerosols on the Juneau Ice Field that are captured in the core. But there are also markers of hardwood combustion, which suggests that aerosols are transported to the ice field from wildfires in upwind areas with hardwood forests like East Asia."
+            paragraph3: "The ratio of levoglucosan to the sum of mannosan and galactosan can be used to distinguish between types of vegetation that burned. Alaska is dominated by softwoods, and <a href='/visualizations/climate-charts/#/fire-in-ice/aerosol-paths' target='_blank'>regional fires</a> likely deposit aerosols on the Juneau Ice Field that are captured in the core. But there are also markers of hardwood combustion, which suggests that aerosols are transported to the ice field from much farther afield. One possible source is wildfires in hardwood forests in East Asia."
         },
         ThreatSankey: {
             paragraph1: 'Land use change is the biggest threat to inland fisheries.'
diff --git a/src/components/WildfireAerosolsViz.vue b/src/components/WildfireAerosolsViz.vue
index b9e0505..bc7ee82 100644
--- a/src/components/WildfireAerosolsViz.vue
+++ b/src/components/WildfireAerosolsViz.vue
@@ -59,7 +59,7 @@
     const chart = ref(null);
     let chartSVG;
     const chartTitle = 'Title of chart';
-    const chartHeight = mobileView ? window.innerHeight * 0.6 : window.innerHeight * 0.7;
+    const chartHeight = mobileView ? window.innerHeight * 0.6 : window.innerHeight * 0.85;
     let chartWidth;
     let chartDimensions;
     let chartBounds;
@@ -128,13 +128,13 @@
                 })
 
                 const defaultMargin = mobileView ? 5 : 10;
-                const sharedTopMargin = mobileView ? 100 : 120;
+                const sharedTopMargin = mobileView ? 120 : 120;
                 const sharedBottomMargin = mobileView ? 0 : 10;
 
                 const tileChartWidth = chartDimensions.boundedWidth / 3
                 tileChartTranslateX1 = mobileView ? 140 : 350;
                 tileChartTranslateX2 = mobileView ? 65 : 200;
-                tileChartTranslateX3 = mobileView ? 60 : 80;
+                tileChartTranslateX3 = mobileView ? 50 : 80;
                 initTileChart({
                     width: tileChartWidth,
                     height: chartHeight,
@@ -148,7 +148,7 @@
 
                 const barChartWidth = chartDimensions.boundedWidth / 3
                 barChartTranslateX2 = mobileView ? tileChartWidth + 55 : tileChartWidth + 150;
-                barChartTranslateX3 = mobileView ? tileChartWidth + 30 : tileChartWidth + 50;
+                barChartTranslateX3 = mobileView ? tileChartWidth + 45 : tileChartWidth + 50;
                 initBarChart({
                     width: barChartWidth,
                     height: chartHeight,
@@ -778,7 +778,7 @@
 
         // append legend rectangle
         const rectWidth = tileChartDimensions.boundedWidth / 2;
-        const rectHeight = tileChartDimensions.margin.top / 4;
+        const rectHeight = mobileView ? tileChartDimensions.margin.top / 6 : tileChartDimensions.margin.top / 4;
         const rectX = tileChartDimensions.boundedWidth / 2 - rectWidth / 2;
         legendGroup.append("rect")
               .attr("class", "c1p2 matrixLegend")
@@ -945,39 +945,44 @@
             
             // Begin right of legend
             let cumulativeWidth = titleWidth;
-            // if (mobileView) {
-                // On mobile, only use preceding items in same row to find cumulative width
+            if (!mobileView) {
                 // row 1: items 0 and 1
                 if (i < 2) {
-                for (let j = 0; j < i; j++) {
-                    cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
-                }
+                    for (let j = 0; j < i; j++) {
+                        cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
+                    }
                 }
-                // row 2: items 2, 3 and 4
-                else if (i < 5) {
-                for (let j = 2; j < i; j++) {
-                    cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
+                // row 2: item 2
+                else if (i < 3) {
+                    for (let j = 2; j < i; j++) {
+                        cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
+                    }
                 }
+            } else {
+                if (i < 1) {
+                    for (let j = 0; j < i; j++) {
+                        cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
+                    }
                 }
-                // row 3: item 5 
-                else if (i === 5) {
-                for (let j = 2; j < i; j++) {
-                    // Actually storing width of row 2 here, to use to set selfSupplyEnd
-                    cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
+                // row 2: item 2
+                else if (i < 2) {
+                    for (let j = 2; j < i; j++) {
+                        cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
+                    }
                 }
+                // row 3: item 3 
+                else if (i === 3) {
+                    for (let j = 2; j < i; j++) {
+                        cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
+                    }
                 }
-            // } else {
-                // on desktop, iterate through all preceding items to find cumulative width, since all items in 1 row
-                // for (let j = 0; j < i; j++) {
-                // cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
-                // }
-            // }
+            }
 
             let yTranslation = 0;
             // Determine x and y translation
             // set y translation for each row
             // adjust row starting position for 2nd and third rows by -titleWidth
-            // if (mobileView) {
+            if (!mobileView) {
                 if (i < 2) {
                     yTranslation = 0;
                 } else if (i < 5) {
@@ -987,7 +992,17 @@
                     yTranslation = legendRectSize * 5.75
                     cumulativeWidth = xBuffer; // for last item just translate by xBuffer
                 } 
-            // }
+            } else {
+                if (i < 1) {
+                    yTranslation = 0;
+                } else if (i < 2) {
+                    yTranslation = legendRectSize * 4;
+                    cumulativeWidth = cumulativeWidth - titleWidth;
+                } else {
+                    yTranslation = legendRectSize * 8
+                    cumulativeWidth = 0; // for last item just translate by xBuffer
+                } 
+            }
 
             // translate each group by that width and height
             return "translate(" + cumulativeWidth + "," + yTranslation + ")"
@@ -1081,7 +1096,7 @@
         legendGroups.append("circle")
             .attr("class", "legend-point")
             .attr("cx", 0)
-            .attr("cy", -scatterChartDimensions.margin.top / 2 - legendPointSize / 2.5)
+            .attr("cy", -scatterChartDimensions.margin.top / 2)
             .attr("r", legendPointSize)
             .style("fill", d => scatterColorScale(d))
         
@@ -1096,7 +1111,6 @@
 
         // Position legend groups
         // https://stackoverflow.com/questions/20224611/d3-position-text-element-dependent-on-length-of-element-before
-        const xBuffer = 6; // set xBuffer for use in mobile row x translations
         legendGroups
             .attr("transform", (d, i) => {
                 // Compute total width of preceeding legend items, with spacing
@@ -1105,49 +1119,61 @@
                 
                 // Begin right of legend
                 let cumulativeWidth = titleWidth;
-                // if (mobileView) {
-                    // On mobile, only use preceding items in same row to find cumulative width
+                if (!mobileView) {
                     // row 1: items 0 and 1
                     if (i < 2) {
-                    for (let j = 0; j < i; j++) {
-                        cumulativeWidth = cumulativeWidth + legendGroups._groups[0][j].getBBox().width + interItemSpacing;
+                        for (let j = 0; j < i; j++) {
+                            cumulativeWidth = cumulativeWidth + legendGroups._groups[0][j].getBBox().width + interItemSpacing;
+                        }
                     }
+                    // row 2: item 2
+                    else if (i < 3) {
+                        for (let j = 2; j < i; j++) {
+                            cumulativeWidth = cumulativeWidth + legendGroups._groups[0][j].getBBox().width + interItemSpacing;
+                        }
                     }
-                    // row 2: items 2, 3 and 4
-                    else if (i < 5) {
-                    for (let j = 2; j < i; j++) {
-                        cumulativeWidth = cumulativeWidth + legendGroups._groups[0][j].getBBox().width + interItemSpacing;
-                    }
+                } else {
+                    if (i < 1) {
+                        for (let j = 0; j < i; j++) {
+                            cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
+                        }
                     }
-                    // row 3: item 5 
-                    else if (i === 5) {
-                    for (let j = 2; j < i; j++) {
-                        // Actually storing width of row 2 here, to use to set selfSupplyEnd
-                        cumulativeWidth = cumulativeWidth + legendGroups._groups[0][j].getBBox().width + interItemSpacing;
+                    // row 2: item 2
+                    else if (i < 2) {
+                        for (let j = 2; j < i; j++) {
+                            cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
+                        }
                     }
+                    // row 3: item 3 
+                    else if (i === 3) {
+                        for (let j = 2; j < i; j++) {
+                            cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
+                        }
                     }
-                // } else {
-                    // on desktop, iterate through all preceding items to find cumulative width, since all items in 1 row
-                    // for (let j = 0; j < i; j++) {
-                    // cumulativeWidth = cumulativeWidth + legendGroups._groups[0][j].getBBox().width + interItemSpacing;
-                    // }
-                // }
+                }
 
                 let yTranslation = 0;
                 // Determine x and y translation
                 // set y translation for each row
                 // adjust row starting position for 2nd and third rows by -titleWidth
-                // if (mobileView) {
+                if (!mobileView) {
                     if (i < 2) {
                         yTranslation = 0;
-                    } else if (i < 5) {
+                    } else if (i < 3) {
                         yTranslation = mobileView ? barYScale.bandwidth() * 4 : barYScale.bandwidth() * 2;
                         cumulativeWidth = cumulativeWidth - titleWidth;
+                    }
+                } else {
+                    if (i < 1) {
+                        yTranslation = 0;
+                    } else if (i < 2) {
+                        yTranslation = barYScale.bandwidth() * 4;
+                        cumulativeWidth = cumulativeWidth - titleWidth;
                     } else {
-                        yTranslation = legendPointSize * 5.75
-                        cumulativeWidth = xBuffer; // for last item just translate by xBuffer
-                    } 
-                // }
+                        yTranslation = barYScale.bandwidth() * 8
+                        cumulativeWidth = 0; // for last item just translate by xBuffer
+                    }
+                }
 
                 // translate each group by that width and height
                 return "translate(" + cumulativeWidth + "," + yTranslation + ")"
@@ -1388,7 +1414,7 @@
         fill: var(--color-text);
         user-select: none;
         @media only screen and (max-width: 600px) {
-            font-size: 1.6rem;
+            font-size: 1.4rem;
         }
     }
     .axis-subtitle {
@@ -1398,7 +1424,7 @@
         fill: var(--color-text);
         user-select: none;
         @media only screen and (max-width: 600px) {
-            font-size: 1.6rem;
+            font-size: 1.4rem;
         }
     }
     .year-bands {
-- 
GitLab


From 0ccb552b060504c716ec969dd254d567615cd1b8 Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Sun, 27 Oct 2024 21:36:32 -0500
Subject: [PATCH 22/38] update linkto viz 3

---
 src/assets/text/text.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/assets/text/text.js b/src/assets/text/text.js
index 75c80f6..e4cc99e 100644
--- a/src/assets/text/text.js
+++ b/src/assets/text/text.js
@@ -179,7 +179,7 @@ export default {
         WildfireAerosols: {
             paragraph1: "Each layer of the <a href='/visualizations/climate-charts/#/fire-in-ice/glacier-scan' target='_blank'>collected ice core</a> contains more than just ice. Particles from the air, called aerosols, that were deposited on the surface of the glacier are preserved in the ice. These aerosols can come from dust, fossil fuel combustion, or wildfires.",
             paragraph2: "Can we tell if any of these particles came from wildfires? Three sugars — levoglucosan, mannosan, and galactosan — are only produced when vegetation burns at high temperatures. These sugars are present throughout the core, which tells us that some of the particles in the ice were deposited by wildfires.",   
-            paragraph3: "The ratio of levoglucosan to the sum of mannosan and galactosan can be used to distinguish between types of vegetation that burned. Alaska is dominated by softwoods, and <a href='/visualizations/climate-charts/#/fire-in-ice/aerosol-paths' target='_blank'>regional fires</a> likely deposit aerosols on the Juneau Ice Field that are captured in the core. But there are also markers of hardwood combustion, which suggests that aerosols are transported to the ice field from much farther afield. One possible source is wildfires in hardwood forests in East Asia."
+            paragraph3: "The ratio of levoglucosan to the sum of mannosan and galactosan can be used to distinguish between types of vegetation that burned. Alaska is dominated by softwoods, and <a href='/visualizations/climate-charts/#/fire-in-ice/regional-fires' target='_blank'>regional fires</a> likely deposit aerosols on the Juneau Ice Field that are captured in the core. But there are also markers of hardwood combustion, which suggests that aerosols are transported to the ice field from much farther afield. One possible source is wildfires in hardwood forests in East Asia."
         },
         ThreatSankey: {
             paragraph1: 'Land use change is the biggest threat to inland fisheries.'
-- 
GitLab


From e5b8b1ad8347d9ac34f4ef112753a17f97284949 Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Mon, 28 Oct 2024 12:28:34 -0500
Subject: [PATCH 23/38] Add thumbnail

---
 src/assets/content/ChartGrid.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/assets/content/ChartGrid.js b/src/assets/content/ChartGrid.js
index b3f8e78..5d7e5a2 100644
--- a/src/assets/content/ChartGrid.js
+++ b/src/assets/content/ChartGrid.js
@@ -47,7 +47,7 @@ export default {
             project: 'Fire in Ice',
             vizKey: 'WildfireAerosols',
             vizRoute: 'wildfire-aerosols',
-            img_src: 'placeholder_thumbnail.png',
+            img_src: 'wildfire_aerosols_thumbnail.png',
             alt: '',
             chartOrder: 2,
             description: 'Wildfires are depositing aerosols on glaciers.'
-- 
GitLab


From 5c397503fe37520c17285b9df2fd54a6bd6e1191 Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Mon, 28 Oct 2024 14:51:06 -0500
Subject: [PATCH 24/38] clean up laptop display

---
 src/components/WildfireAerosolsViz.vue | 45 ++++++++++++++------------
 1 file changed, 25 insertions(+), 20 deletions(-)

diff --git a/src/components/WildfireAerosolsViz.vue b/src/components/WildfireAerosolsViz.vue
index bc7ee82..fa774a8 100644
--- a/src/components/WildfireAerosolsViz.vue
+++ b/src/components/WildfireAerosolsViz.vue
@@ -131,40 +131,40 @@
                 const sharedTopMargin = mobileView ? 120 : 120;
                 const sharedBottomMargin = mobileView ? 0 : 10;
 
-                const tileChartWidth = chartDimensions.boundedWidth / 3
-                tileChartTranslateX1 = mobileView ? 140 : 350;
-                tileChartTranslateX2 = mobileView ? 65 : 200;
+                const tileChartWidth = mobileView ? chartDimensions.boundedWidth / 3 : chartDimensions.boundedWidth / 4
+                tileChartTranslateX1 = mobileView ? 140 : (chartDimensions.boundedWidth - tileChartWidth) / 2;
+                tileChartTranslateX2 = mobileView ? 65 : 240;
                 tileChartTranslateX3 = mobileView ? 50 : 80;
                 initTileChart({
                     width: tileChartWidth,
                     height: chartHeight,
                     margin: defaultMargin,
-                    marginLeft: mobileView ? 55: 80,
-                    marginRight: mobileView ? 5: 40,
+                    marginLeft: mobileView ? 55: 20,
+                    marginRight: mobileView ? 5: 20,
                     marginTop: sharedTopMargin,
                     marginBottom: sharedBottomMargin,
                     translateX: tileChartTranslateX1
                 });
 
                 const barChartWidth = chartDimensions.boundedWidth / 3
-                barChartTranslateX2 = mobileView ? tileChartWidth + 55 : tileChartWidth + 150;
-                barChartTranslateX3 = mobileView ? tileChartWidth + 45 : tileChartWidth + 50;
+                barChartTranslateX2 = mobileView ? tileChartWidth + 55 : tileChartTranslateX2 + tileChartWidth;
+                barChartTranslateX3 = mobileView ? tileChartWidth + 45 : tileChartTranslateX3 + tileChartWidth;
                 initBarChart({
                     width: barChartWidth,
                     height: chartHeight,
                     margin: defaultMargin,
-                    marginLeft: mobileView ? 20 : 80,
+                    marginLeft: mobileView ? 20 : 60,
                     marginTop: sharedTopMargin,
                     marginBottom: sharedBottomMargin,
                     translateX: barChartTranslateX2});
 
                 const scatterChartWidth = chartDimensions.boundedWidth / 3
-                scatterChartTranslateX3 = mobileView ? tileChartWidth + barChartWidth + 50 : tileChartWidth + barChartWidth + 80;
+                scatterChartTranslateX3 = mobileView ? tileChartWidth + barChartWidth + 50 : tileChartTranslateX3 + tileChartWidth + barChartWidth;
                 initScatterChart({
                     width: scatterChartWidth,
                     height: chartHeight,
                     margin: defaultMargin,
-                    marginLeft: mobileView ? 20 : scatterChartWidth / 3,
+                    marginLeft: mobileView ? 20 : 10,
                     marginRight: scatterChartWidth / 3,
                     marginTop: sharedTopMargin,
                     marginBottom: sharedBottomMargin,
@@ -178,13 +178,13 @@
                 maskingRect = chartSVG
                     .append("rect")
                     .attr("id", "masking-rect")
-                    .attr("x", tileChartTranslateX1 + tileChartDimensions.width)
+                    .attr("x", 0)
                     .attr("y", 0)
                     .attr("width", barChartDimensions.width + scatterChartDimensions.width)
                     .attr("height", chartHeight)
                     .attr("fill", bkgdColor)
                     .style("transform", `translate(${
-                        mobileView ? -30 : -100
+                        tileChartTranslateX1 + tileChartDimensions.width
                     }px, 0px)`);
                 drawScatterChart(scatterData.value);
                 scatterChartBounds
@@ -676,7 +676,7 @@
             {
                 tickFormat: ".0f", 
                 customSuffix: 'cm', 
-                tickSize: -chartDimensions.boundedWidth, 
+                tickSize: -chartDimensions.boundedWidth - tileChartTranslateX1 - tileChartDimensions.margin.left, 
                 keepDomain: false
             }
         )
@@ -986,7 +986,7 @@
                 if (i < 2) {
                     yTranslation = 0;
                 } else if (i < 5) {
-                    yTranslation = mobileView ? legendRectSize * 4 : legendRectSize * 2;
+                    yTranslation = window.innerHeight < 770 ? legendRectSize * 4 : legendRectSize * 2;
                     cumulativeWidth = cumulativeWidth - titleWidth;
                 } else {
                     yTranslation = legendRectSize * 5.75
@@ -1044,6 +1044,7 @@
         /////    ADD CHART ELEMENTS    /////
         ////////////////////////////////////
         // draw chart
+        const desktopPointSize = window.innerHeight < 770 ? 2 : 4;
         scatterChartBounds.select('.points') // selects our group we set up to hold chart elements
             .selectAll(".point") // empty selection
                 .data(data) // bind data
@@ -1053,7 +1054,7 @@
                     .attr("id", d => 'point-' + identifierAccessor(d))
                     .attr("cx", d => scatterXScale(xAccessor(d)))
                     .attr("cy", d => barYScale(yAccessor(d)) + barYScale.bandwidth()/2)
-                    .attr("r", mobileView ? 2 : 4)
+                    .attr("r", mobileView ? 2 : desktopPointSize)
                     .style("fill", d => scatterColorScale(colorAccessor(d)));
     }
 
@@ -1075,7 +1076,8 @@
             .text('Burned vegetation type')
             .call(d => wrap(d))
 
-        const legendPointSize = mobileView ? 2 : 4;
+        const desktopPointSize = window.innerHeight < 770 ? 2 : 4;
+        const legendPointSize = mobileView ? 2 : desktopPointSize;
         const interItemSpacing = mobileView ? 15 : 10;
         const intraItemSpacing = 6;
 
@@ -1160,7 +1162,7 @@
                     if (i < 2) {
                         yTranslation = 0;
                     } else if (i < 3) {
-                        yTranslation = mobileView ? barYScale.bandwidth() * 4 : barYScale.bandwidth() * 2;
+                        yTranslation = window.innerHeight < 770 ? barYScale.bandwidth() * 4 : barYScale.bandwidth() * 2;
                         cumulativeWidth = cumulativeWidth - titleWidth;
                     }
                 } else {
@@ -1225,7 +1227,7 @@
                 .duration(transitionLength)
                 .delay(transitionLength / 4)
                 .style("transform", `translate(${
-                    mobileView ? -30 : -100
+                    tileChartTranslateX1 + tileChartDimensions.width
                 }px, 0px)`);
             moveChart(tileChartBounds, tileChartTranslateX1, tileChartDimensions.margin.top)
             if (!barChartHidden) hideChart(barChartBounds)            
@@ -1233,7 +1235,7 @@
             console.log(barChartDimensions.width)
             maskingRect
                 .style("transform", `translate(${
-                    mobileView ? barChartDimensions.width : 0
+                    tileChartTranslateX2 + tileChartDimensions.width + barChartDimensions.width
                 }px, 0px)`)
                 .transition()
                 .duration(transitionLength)
@@ -1309,7 +1311,7 @@
 <style scoped lang="scss">
     #wildfire-aerosols-grid-container {
         display: grid;
-        max-width: 1200px;
+        max-width: 900px;
         grid-template-columns: 10% calc(80% - 4rem) 10%;
         grid-template-rows: auto max-content;
         grid-template-areas:
@@ -1450,5 +1452,8 @@
         @media only screen and (max-width: 600px) {
             font-size: 0.8rem;
         }
+        @media screen and (max-height: 770px) { 
+            font-size: 0.8rem;
+        }
     }
 </style>
\ No newline at end of file
-- 
GitLab


From 2f31dc965caa34ecc910a1210e39cd5340be0015 Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Mon, 28 Oct 2024 17:22:39 -0500
Subject: [PATCH 25/38] merge in upstream and fix conflicts

---
 src/assets/content/ChartGrid.js | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/src/assets/content/ChartGrid.js b/src/assets/content/ChartGrid.js
index 44bab74..d1e1502 100644
--- a/src/assets/content/ChartGrid.js
+++ b/src/assets/content/ChartGrid.js
@@ -59,13 +59,8 @@ export default {
             vizRoute: 'regional-fires',
             img_src: 'aerosols_map_thumbnail.png',
             alt: '',
-<<<<<<< HEAD
-            chartOrder: 3,
-            description: 'Wildfire particles are deposited on glaciers.'
-=======
             chartOrder: 2,
             description: 'Particles from regional wildfires are deposited on glaciers.'
->>>>>>> d81ef8f2f4449e747a8cb570393dedafec549dab
         },
         {
             title: 'Global economic value of recreationally fished species',
-- 
GitLab


From f89756449d1a3e382d90c0ab3aefc4a87a71614a Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Mon, 28 Oct 2024 17:24:32 -0500
Subject: [PATCH 26/38] suggested text edits

---
 src/assets/text/text.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/assets/text/text.js b/src/assets/text/text.js
index 0db57f6..c959049 100644
--- a/src/assets/text/text.js
+++ b/src/assets/text/text.js
@@ -178,7 +178,7 @@ export default {
         },
         WildfireAerosols: {
             paragraph1: "Each layer of the <a href='/visualizations/climate-charts/#/fire-in-ice/glacier-scan' target='_blank'>collected ice core</a> contains more than just ice. Particles from the air, called aerosols, that were deposited on the surface of the glacier are preserved in the ice. These aerosols can come from dust, fossil fuel combustion, or wildfires.",
-            paragraph2: "Can we tell if any of these particles came from wildfires? Three sugars — levoglucosan, mannosan, and galactosan — are only produced when vegetation burns at high temperatures. These sugars are present throughout the core, which tells us that some of the particles in the ice were deposited by wildfires.",   
+            paragraph2: "Can we tell if any of these particles came from wildfires? Three sugars — mannosan, galactosan, and levoglucosan — are only produced when vegetation burns at high temperatures. These sugars are present throughout the core, which tells us that some of the deposited particles in the ice were sourced from wildfires.",   
             paragraph3: "The ratio of levoglucosan to the sum of mannosan and galactosan can be used to distinguish between types of vegetation that burned. Alaska is dominated by softwoods, and <a href='/visualizations/climate-charts/#/fire-in-ice/regional-fires' target='_blank'>regional fires</a> likely deposit aerosols on the Juneau Ice Field that are captured in the core. But there are also markers of hardwood combustion, which suggests that aerosols are transported to the ice field from much farther afield. One possible source is wildfires in hardwood forests in East Asia."
         },
         ThreatSankey: {
-- 
GitLab


From 405dc3f10ee6ba4e6bcbd8b86058e25b3b9ca2fa Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Mon, 28 Oct 2024 17:26:09 -0500
Subject: [PATCH 27/38] suggested text edits

---
 src/assets/text/text.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/assets/text/text.js b/src/assets/text/text.js
index c959049..7cc6bd5 100644
--- a/src/assets/text/text.js
+++ b/src/assets/text/text.js
@@ -177,7 +177,7 @@ export default {
             paragraph1: 'Explore total recreational harvest for the five families of inland fish with the largest recreational harvests: <span class="scientificName">Cyprinidae</span> (minnows and carps), <span class="scientificName">Percidae</span> (perch), <span class="scientificName">Salmonidae</span> (salmon, trout, grayling, and whitefish), <span class="scientificName">Bagridae</span> (bagrid catfish), and <span class="scientificName">Centrarchidae</span> (sunfishes). Total recreational harvest is broken out by family, by species, and by country.  Hover over the chart to see the harvest totals, in kilograms'
         },
         WildfireAerosols: {
-            paragraph1: "Each layer of the <a href='/visualizations/climate-charts/#/fire-in-ice/glacier-scan' target='_blank'>collected ice core</a> contains more than just ice. Particles from the air, called aerosols, that were deposited on the surface of the glacier are preserved in the ice. These aerosols can come from dust, fossil fuel combustion, or wildfires.",
+            paragraph1: "Each layer of the <a href='/visualizations/climate-charts/#/fire-in-ice/glacier-scan' target='_blank'>collected ice core</a> contains more than just ice. Particles from the air, called aerosols, deposit on the surface of the glacier. These aerosols can come from dust, fossil fuel combustion, or wildfires. When snow buries the deposited particles, they are preserved in the ice.",
             paragraph2: "Can we tell if any of these particles came from wildfires? Three sugars — mannosan, galactosan, and levoglucosan — are only produced when vegetation burns at high temperatures. These sugars are present throughout the core, which tells us that some of the deposited particles in the ice were sourced from wildfires.",   
             paragraph3: "The ratio of levoglucosan to the sum of mannosan and galactosan can be used to distinguish between types of vegetation that burned. Alaska is dominated by softwoods, and <a href='/visualizations/climate-charts/#/fire-in-ice/regional-fires' target='_blank'>regional fires</a> likely deposit aerosols on the Juneau Ice Field that are captured in the core. But there are also markers of hardwood combustion, which suggests that aerosols are transported to the ice field from much farther afield. One possible source is wildfires in hardwood forests in East Asia."
         },
-- 
GitLab


From 15eaec2b8c72a965611ebbc3c0f807da4882b3b2 Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Mon, 28 Oct 2024 17:27:45 -0500
Subject: [PATCH 28/38] suggested text edits

---
 src/assets/text/text.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/assets/text/text.js b/src/assets/text/text.js
index 7cc6bd5..d7bdf1b 100644
--- a/src/assets/text/text.js
+++ b/src/assets/text/text.js
@@ -179,7 +179,7 @@ export default {
         WildfireAerosols: {
             paragraph1: "Each layer of the <a href='/visualizations/climate-charts/#/fire-in-ice/glacier-scan' target='_blank'>collected ice core</a> contains more than just ice. Particles from the air, called aerosols, deposit on the surface of the glacier. These aerosols can come from dust, fossil fuel combustion, or wildfires. When snow buries the deposited particles, they are preserved in the ice.",
             paragraph2: "Can we tell if any of these particles came from wildfires? Three sugars — mannosan, galactosan, and levoglucosan — are only produced when vegetation burns at high temperatures. These sugars are present throughout the core, which tells us that some of the deposited particles in the ice were sourced from wildfires.",   
-            paragraph3: "The ratio of levoglucosan to the sum of mannosan and galactosan can be used to distinguish between types of vegetation that burned. Alaska is dominated by softwoods, and <a href='/visualizations/climate-charts/#/fire-in-ice/regional-fires' target='_blank'>regional fires</a> likely deposit aerosols on the Juneau Ice Field that are captured in the core. But there are also markers of hardwood combustion, which suggests that aerosols are transported to the ice field from much farther afield. One possible source is wildfires in hardwood forests in East Asia."
+            paragraph3: "Scientists use the ratio of levoglucosan to the sum of mannosan and galactosan to distinguish between types of vegetation that burned. Alaska's forests are dominated by softwoods, and <a href='/visualizations/climate-charts/#/fire-in-ice/regional-fires' target='_blank'>regional fires</a> likely deposit aerosols on the Juneau Ice Field that are captured in the core. However, there are also markers of hardwood combustion, which suggests that aerosols are transported to the ice field from much farther afield. One possible source is wildfires in hardwood forests in East Asia."
         },
         ThreatSankey: {
             paragraph1: 'Land use change is the biggest threat to inland fisheries.'
-- 
GitLab


From bb92d8d992504db3955c064d73970b4752bb85e5 Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Mon, 28 Oct 2024 17:31:29 -0500
Subject: [PATCH 29/38] up # of tick marks

---
 src/components/WildfireAerosolsViz.vue | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/components/WildfireAerosolsViz.vue b/src/components/WildfireAerosolsViz.vue
index fa774a8..4e61095 100644
--- a/src/components/WildfireAerosolsViz.vue
+++ b/src/components/WildfireAerosolsViz.vue
@@ -677,6 +677,7 @@
                 tickFormat: ".0f", 
                 customSuffix: 'cm', 
                 tickSize: -chartDimensions.boundedWidth - tileChartTranslateX1 - tileChartDimensions.margin.left, 
+                nticks: 20,
                 keepDomain: false
             }
         )
-- 
GitLab


From 51c5b6ceeb31dbcc53258db35a32b69aac019038 Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Mon, 28 Oct 2024 18:02:26 -0500
Subject: [PATCH 30/38] Drop console statement

---
 src/components/WildfireAerosolsViz.vue | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/components/WildfireAerosolsViz.vue b/src/components/WildfireAerosolsViz.vue
index 4e61095..f722111 100644
--- a/src/components/WildfireAerosolsViz.vue
+++ b/src/components/WildfireAerosolsViz.vue
@@ -1233,7 +1233,6 @@
             moveChart(tileChartBounds, tileChartTranslateX1, tileChartDimensions.margin.top)
             if (!barChartHidden) hideChart(barChartBounds)            
         } else if (index == 2) {
-            console.log(barChartDimensions.width)
             maskingRect
                 .style("transform", `translate(${
                     tileChartTranslateX2 + tileChartDimensions.width + barChartDimensions.width
-- 
GitLab


From 69949d12a05f0997e98e21952a785aaa57c5662c Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Tue, 29 Oct 2024 10:01:48 -0500
Subject: [PATCH 31/38] try setting max height for chart

---
 src/components/WildfireAerosolsViz.vue | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/components/WildfireAerosolsViz.vue b/src/components/WildfireAerosolsViz.vue
index f722111..004819c 100644
--- a/src/components/WildfireAerosolsViz.vue
+++ b/src/components/WildfireAerosolsViz.vue
@@ -59,7 +59,7 @@
     const chart = ref(null);
     let chartSVG;
     const chartTitle = 'Title of chart';
-    const chartHeight = mobileView ? window.innerHeight * 0.6 : window.innerHeight * 0.85;
+    let chartHeight;
     let chartWidth;
     let chartDimensions;
     let chartBounds;
@@ -120,6 +120,9 @@
             if (tileData.value.length > 0 && barData.value.length > 0) {
                 
                 // initialize chart elements
+                // on desktop, don't let chart height exceed 1200px
+                const desktopHeight = window.innerHeight < 770 ? window.innerHeight * 0.85 : Math.min(window.innerHeight * 0.75, 1000);
+                chartHeight = mobileView ? window.innerHeight * 0.6 : desktopHeight;
                 chartWidth = chart.value.offsetWidth;
                 initChart({
                     width: chartWidth,
-- 
GitLab


From a0dbec15d3fec05e310e8684ae5abecfff511fb1 Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Tue, 29 Oct 2024 10:42:00 -0500
Subject: [PATCH 32/38] further reduce max chart height

---
 src/components/WildfireAerosolsViz.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/components/WildfireAerosolsViz.vue b/src/components/WildfireAerosolsViz.vue
index 004819c..f9bce3b 100644
--- a/src/components/WildfireAerosolsViz.vue
+++ b/src/components/WildfireAerosolsViz.vue
@@ -121,7 +121,7 @@
                 
                 // initialize chart elements
                 // on desktop, don't let chart height exceed 1200px
-                const desktopHeight = window.innerHeight < 770 ? window.innerHeight * 0.85 : Math.min(window.innerHeight * 0.75, 1000);
+                const desktopHeight = window.innerHeight < 770 ? window.innerHeight * 0.85 : Math.min(window.innerHeight * 0.75, 800);
                 chartHeight = mobileView ? window.innerHeight * 0.6 : desktopHeight;
                 chartWidth = chart.value.offsetWidth;
                 initChart({
-- 
GitLab


From cebf22f944a7b6a5ad36da3be37e200bf67155f9 Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Tue, 29 Oct 2024 10:56:22 -0500
Subject: [PATCH 33/38] change how masking rect color is defined

---
 src/components/WildfireAerosolsViz.vue | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/components/WildfireAerosolsViz.vue b/src/components/WildfireAerosolsViz.vue
index f9bce3b..22f70a0 100644
--- a/src/components/WildfireAerosolsViz.vue
+++ b/src/components/WildfireAerosolsViz.vue
@@ -45,8 +45,6 @@
     // global variables
     const publicPath = import.meta.env.BASE_URL;
     const mobileView = isMobile;
-    const bodyCSS = window.getComputedStyle(document.body)
-    const bkgdColor = bodyCSS.getPropertyValue('--color-background');
     const tileDataFile = 'fii_core4particulates.csv';
     const barDataFile = 'fii_core4sugars.csv';
     const scatterDataFile = 'fii_core4biomass.csv';
@@ -185,7 +183,6 @@
                     .attr("y", 0)
                     .attr("width", barChartDimensions.width + scatterChartDimensions.width)
                     .attr("height", chartHeight)
-                    .attr("fill", bkgdColor)
                     .style("transform", `translate(${
                         tileChartTranslateX1 + tileChartDimensions.width
                     }px, 0px)`);
@@ -1403,6 +1400,9 @@
 </style>
 <style lang="scss">
 /* css for elements added/classed w/ d3 */
+    #masking-rect {
+        fill: var(--color-background);
+    }
     .axis-text {
         font-size: 1.6rem;
         font-weight: 300;
-- 
GitLab


From abeb2a07f1b8f38f88edf6c8ffefbaff7b79b697 Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Tue, 29 Oct 2024 12:17:34 -0500
Subject: [PATCH 34/38] make bar and scatter legends 3 rows on desktop

---
 src/components/WildfireAerosolsViz.vue | 246 +++++++++++++------------
 1 file changed, 125 insertions(+), 121 deletions(-)

diff --git a/src/components/WildfireAerosolsViz.vue b/src/components/WildfireAerosolsViz.vue
index 22f70a0..1b8ab54 100644
--- a/src/components/WildfireAerosolsViz.vue
+++ b/src/components/WildfireAerosolsViz.vue
@@ -129,7 +129,7 @@
                 })
 
                 const defaultMargin = mobileView ? 5 : 10;
-                const sharedTopMargin = mobileView ? 120 : 120;
+                const sharedTopMargin = mobileView ? 135 : 130;
                 const sharedBottomMargin = mobileView ? 0 : 10;
 
                 const tileChartWidth = mobileView ? chartDimensions.boundedWidth / 3 : chartDimensions.boundedWidth / 4
@@ -907,7 +907,7 @@
         //     .attr("dominant-baseline", "central")
 
         const legendRectSize = barYScale.bandwidth();
-        const interItemSpacing = mobileView ? 15 : 10;
+        // const interItemSpacing = mobileView ? 15 : 10;
         const intraItemSpacing = 6;
 
         // Append group for each legend entry
@@ -921,7 +921,7 @@
         legendGroup.append("rect")
             .attr("class", "legend-rect")
             .attr("x", 0)
-            .attr("y", -barChartDimensions.margin.top / 2 - legendRectSize / 2.5)
+            .attr("y", -barChartDimensions.margin.top / 1.75 - legendRectSize / 2.5)
             .attr("width", legendRectSize)
             .attr("height", legendRectSize)
             .style("fill", d => barColorScale(d))
@@ -930,80 +930,82 @@
         legendGroup.append("text")
             .attr("class", "legend-text")
             .attr("x", legendRectSize + intraItemSpacing) // put text to the right of the rectangle
-            .attr("y", -barChartDimensions.margin.top / 2)
+            .attr("y", -barChartDimensions.margin.top / 1.75)
             .attr("text-anchor", "start") // left-align text
             .attr("dominant-baseline", "central")
             .text(d => d);
 
         // Position legend groups
         // https://stackoverflow.com/questions/20224611/d3-position-text-element-dependent-on-length-of-element-before
-        const xBuffer = 6; // set xBuffer for use in mobile row x translations
+        // const xBuffer = 6; // set xBuffer for use in mobile row x translations
         legendGroup
             .attr("transform", (d, i) => {
             // Compute total width of preceeding legend items, with spacing
             // Start with width of legend title
-            const titleWidth = 0 //d3.select('#legend-title')._groups[0][0].getBBox().width + interItemSpacing;
+            // const d3.select('#legend-title')._groups[0][0].getBBox().width + interItemSpacing;
             
             // Begin right of legend
-            let cumulativeWidth = titleWidth;
-            if (!mobileView) {
-                // row 1: items 0 and 1
-                if (i < 2) {
-                    for (let j = 0; j < i; j++) {
-                        cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
-                    }
-                }
-                // row 2: item 2
-                else if (i < 3) {
-                    for (let j = 2; j < i; j++) {
-                        cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
-                    }
-                }
-            } else {
-                if (i < 1) {
-                    for (let j = 0; j < i; j++) {
-                        cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
-                    }
-                }
-                // row 2: item 2
-                else if (i < 2) {
-                    for (let j = 2; j < i; j++) {
-                        cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
-                    }
-                }
-                // row 3: item 3 
-                else if (i === 3) {
-                    for (let j = 2; j < i; j++) {
-                        cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
-                    }
-                }
-            }
+            const cumulativeWidth = 0;
+            // let cumulativeWidth = titleWidth;
+            // if (!mobileView) {
+            //     // row 1: items 0 and 1
+            //     if (i < 2) {
+            //         for (let j = 0; j < i; j++) {
+            //             cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
+            //         }
+            //     }
+            //     // row 2: item 2
+            //     else if (i < 3) {
+            //         for (let j = 2; j < i; j++) {
+            //             cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
+            //         }
+            //     }
+            // } else {
+                // if (i < 1) {
+                //     for (let j = 0; j < i; j++) {
+                //         cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
+                //     }
+                // }
+                // // row 2: item 2
+                // else if (i < 2) {
+                //     for (let j = 2; j < i; j++) {
+                //         cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
+                //     }
+                // }
+                // // row 3: item 3 
+                // else if (i === 3) {
+                //     for (let j = 2; j < i; j++) {
+                //         cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
+                //     }
+                // }
+            // }
 
-            let yTranslation = 0;
             // Determine x and y translation
             // set y translation for each row
-            // adjust row starting position for 2nd and third rows by -titleWidth
-            if (!mobileView) {
-                if (i < 2) {
-                    yTranslation = 0;
-                } else if (i < 5) {
-                    yTranslation = window.innerHeight < 770 ? legendRectSize * 4 : legendRectSize * 2;
-                    cumulativeWidth = cumulativeWidth - titleWidth;
-                } else {
-                    yTranslation = legendRectSize * 5.75
-                    cumulativeWidth = xBuffer; // for last item just translate by xBuffer
-                } 
-            } else {
-                if (i < 1) {
-                    yTranslation = 0;
-                } else if (i < 2) {
-                    yTranslation = legendRectSize * 4;
-                    cumulativeWidth = cumulativeWidth - titleWidth;
-                } else {
-                    yTranslation = legendRectSize * 8
-                    cumulativeWidth = 0; // for last item just translate by xBuffer
-                } 
-            }
+            let rowHeight = window.innerHeight < 770 ? legendRectSize * 4 : legendRectSize * 2;
+            const yTranslation = rowHeight * i;
+            // let yTranslation = 0;
+            // if (!mobileView) {
+            //     if (i < 2) {
+            //         yTranslation = 0;
+            //     } else if (i < 5) {
+            //         yTranslation = window.innerHeight < 770 ? legendRectSize * 4 : legendRectSize * 2;
+            //         cumulativeWidth = cumulativeWidth - titleWidth;
+            //     } else {
+            //         yTranslation = legendRectSize * 5.75
+            //         cumulativeWidth = xBuffer; // for last item just translate by xBuffer
+            //     } 
+            // } else {
+                // if (i < 1) {
+                //     yTranslation = 0;
+                // } else if (i < 2) {
+                //     yTranslation = window.innerHeight < 770 ? legendRectSize * 4 : legendRectSize * 2;
+                //     cumulativeWidth = cumulativeWidth - titleWidth;
+                // } else {
+                //     yTranslation = window.innerHeight < 770 ? legendRectSize * 8 : legendRectSize * 4;
+                //     cumulativeWidth = 0; // for last item just translate by xBuffer
+                // } 
+            // }
 
             // translate each group by that width and height
             return "translate(" + cumulativeWidth + "," + yTranslation + ")"
@@ -1079,7 +1081,7 @@
 
         const desktopPointSize = window.innerHeight < 770 ? 2 : 4;
         const legendPointSize = mobileView ? 2 : desktopPointSize;
-        const interItemSpacing = mobileView ? 15 : 10;
+        // const interItemSpacing = mobileView ? 15 : 10;
         const intraItemSpacing = 6;
 
         // Append group for each legend entry
@@ -1095,11 +1097,11 @@
             .append("g")
             .attr("class", "legend-item")
 
-        // Add rectangles for each group
+        // Add points for each group
         legendGroups.append("circle")
             .attr("class", "legend-point")
             .attr("cx", 0)
-            .attr("cy", -scatterChartDimensions.margin.top / 2)
+            .attr("cy", -scatterChartDimensions.margin.top / 1.75 + legendPointSize / 1.5)
             .attr("r", legendPointSize)
             .style("fill", d => scatterColorScale(d))
         
@@ -1107,7 +1109,7 @@
         legendGroups.append("text")
             .attr("class", "legend-text")
             .attr("x", legendPointSize + intraItemSpacing) // put text to the right of the rectangle
-            .attr("y", -scatterChartDimensions.margin.top / 2)
+            .attr("y", -scatterChartDimensions.margin.top / 1.75)
             .attr("text-anchor", "start") // left-align text
             .attr("dominant-baseline", "central")
             .text(d => d);
@@ -1118,65 +1120,67 @@
             .attr("transform", (d, i) => {
                 // Compute total width of preceeding legend items, with spacing
                 // Start with width of legend title
-                const titleWidth = 0 //d3.select('#legend-title')._groups[0][0].getBBox().width + interItemSpacing;
+                // const titleWidth = d3.select('#legend-title')._groups[0][0].getBBox().width + interItemSpacing;
                 
                 // Begin right of legend
-                let cumulativeWidth = titleWidth;
-                if (!mobileView) {
-                    // row 1: items 0 and 1
-                    if (i < 2) {
-                        for (let j = 0; j < i; j++) {
-                            cumulativeWidth = cumulativeWidth + legendGroups._groups[0][j].getBBox().width + interItemSpacing;
-                        }
-                    }
-                    // row 2: item 2
-                    else if (i < 3) {
-                        for (let j = 2; j < i; j++) {
-                            cumulativeWidth = cumulativeWidth + legendGroups._groups[0][j].getBBox().width + interItemSpacing;
-                        }
-                    }
-                } else {
-                    if (i < 1) {
-                        for (let j = 0; j < i; j++) {
-                            cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
-                        }
-                    }
-                    // row 2: item 2
-                    else if (i < 2) {
-                        for (let j = 2; j < i; j++) {
-                            cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
-                        }
-                    }
-                    // row 3: item 3 
-                    else if (i === 3) {
-                        for (let j = 2; j < i; j++) {
-                            cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
-                        }
-                    }
-                }
+                const cumulativeWidth = 0;
+                // let cumulativeWidth = titleWidth;
+                // if (!mobileView) {
+                //     // row 1: items 0 and 1
+                //     if (i < 2) {
+                //         for (let j = 0; j < i; j++) {
+                //             cumulativeWidth = cumulativeWidth + legendGroups._groups[0][j].getBBox().width + interItemSpacing;
+                //         }
+                //     }
+                //     // row 2: item 2
+                //     else if (i < 3) {
+                //         for (let j = 2; j < i; j++) {
+                //             cumulativeWidth = cumulativeWidth + legendGroups._groups[0][j].getBBox().width + interItemSpacing;
+                //         }
+                //     }
+                // } else {
+                    // if (i < 1) {
+                    //     for (let j = 0; j < i; j++) {
+                    //         cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
+                    //     }
+                    // }
+                    // // row 2: item 2
+                    // else if (i < 2) {
+                    //     for (let j = 2; j < i; j++) {
+                    //         cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
+                    //     }
+                    // }
+                    // // row 3: item 3 
+                    // else if (i === 3) {
+                    //     for (let j = 2; j < i; j++) {
+                    //         cumulativeWidth = cumulativeWidth + legendGroup._groups[0][j].getBBox().width + interItemSpacing;
+                    //     }
+                    // }
+                // }
 
-                let yTranslation = 0;
                 // Determine x and y translation
-                // set y translation for each row
-                // adjust row starting position for 2nd and third rows by -titleWidth
-                if (!mobileView) {
-                    if (i < 2) {
-                        yTranslation = 0;
-                    } else if (i < 3) {
-                        yTranslation = window.innerHeight < 770 ? barYScale.bandwidth() * 4 : barYScale.bandwidth() * 2;
-                        cumulativeWidth = cumulativeWidth - titleWidth;
-                    }
-                } else {
-                    if (i < 1) {
-                        yTranslation = 0;
-                    } else if (i < 2) {
-                        yTranslation = barYScale.bandwidth() * 4;
-                        cumulativeWidth = cumulativeWidth - titleWidth;
-                    } else {
-                        yTranslation = barYScale.bandwidth() * 8
-                        cumulativeWidth = 0; // for last item just translate by xBuffer
-                    }
-                }
+                // set y translation for each row               
+                let rowHeight = window.innerHeight < 770 ? barYScale.bandwidth() * 4 : barYScale.bandwidth() * 2;
+                const yTranslation = rowHeight * i;
+                // let yTranslation = 0;
+                // if (!mobileView) {
+                //     if (i < 2) {
+                //         yTranslation = 0;
+                //     } else if (i < 3) {
+                //         yTranslation = window.innerHeight < 770 ? barYScale.bandwidth() * 4 : barYScale.bandwidth() * 2;
+                //         cumulativeWidth = cumulativeWidth - titleWidth;
+                //     }
+                // } else {
+                    // if (i < 1) {
+                    //     yTranslation = 0;
+                    // } else if (i < 2) {
+                    //     yTranslation = barYScale.bandwidth() * 4;
+                    //     cumulativeWidth = cumulativeWidth - titleWidth;
+                    // } else {
+                    //     yTranslation = barYScale.bandwidth() * 8
+                    //     cumulativeWidth = 0; // for last item just translate by xBuffer
+                    // }
+                // }
 
                 // translate each group by that width and height
                 return "translate(" + cumulativeWidth + "," + yTranslation + ")"
-- 
GitLab


From b99b0386afa8e96057a4732e2692c7a1a8ed8349 Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Tue, 29 Oct 2024 13:31:14 -0500
Subject: [PATCH 35/38] clean up translations and structure

---
 src/components/WildfireAerosolsViz.vue | 157 +++++++++++++++----------
 1 file changed, 96 insertions(+), 61 deletions(-)

diff --git a/src/components/WildfireAerosolsViz.vue b/src/components/WildfireAerosolsViz.vue
index 1b8ab54..621e2ca 100644
--- a/src/components/WildfireAerosolsViz.vue
+++ b/src/components/WildfireAerosolsViz.vue
@@ -61,11 +61,13 @@
     let chartWidth;
     let chartDimensions;
     let chartBounds;
+    let chartGap;
     let maskingRect;
     let tileChartTranslateX1;
     let tileChartTranslateX2;
     let tileChartTranslateX3;
     let tileChartDimensions;
+    let tileChartWrapper;
     let tileChartBounds;
     let tileColorScale;
     let yScale;
@@ -73,6 +75,7 @@
     let barChartTranslateX2;
     let barChartTranslateX3;
     let barChartDimensions;
+    let barChartWrapper;
     let barChartBounds;
     let barXScale;
     let barYScale;
@@ -83,6 +86,7 @@
     let barColorScale;
     let scatterChartTranslateX3;
     let scatterChartDimensions;
+    let scatterChartWrapper;
     let scatterChartBounds;
     let scatterXScale;
     let scatterColorCategories;
@@ -125,48 +129,51 @@
                 initChart({
                     width: chartWidth,
                     height: chartHeight,
-                    margin: 0
+                    margin: 30
                 })
 
                 const defaultMargin = mobileView ? 5 : 10;
                 const sharedTopMargin = mobileView ? 135 : 130;
                 const sharedBottomMargin = mobileView ? 0 : 10;
 
-                const tileChartWidth = mobileView ? chartDimensions.boundedWidth / 3 : chartDimensions.boundedWidth / 4
-                tileChartTranslateX1 = mobileView ? 140 : (chartDimensions.boundedWidth - tileChartWidth) / 2;
-                tileChartTranslateX2 = mobileView ? 65 : 240;
-                tileChartTranslateX3 = mobileView ? 50 : 80;
+                chartGap = chartDimensions.boundedWidth / 11
+                const tileChartWidth = mobileView ? chartGap * 3 : chartGap * 3;
+                const barChartWidth = mobileView ? chartGap * 3 : chartGap * 3;
+                const scatterChartWidth = mobileView ? chartGap * 2 : chartGap * 2;
+
+                tileChartTranslateX1 = mobileView ? (chartDimensions.boundedWidth - tileChartWidth) / 2: (chartDimensions.boundedWidth - tileChartWidth) / 2;
+                tileChartTranslateX2 = mobileView ? tileChartTranslateX1 - barChartWidth / 2 : tileChartTranslateX1 - barChartWidth / 2;
+                tileChartTranslateX3 = mobileView ? tileChartTranslateX2 - scatterChartWidth / 2 : tileChartTranslateX2 - scatterChartWidth / 2;
                 initTileChart({
                     width: tileChartWidth,
                     height: chartHeight,
                     margin: defaultMargin,
-                    marginLeft: mobileView ? 55: 20,
-                    marginRight: mobileView ? 5: 20,
+                    marginLeft: mobileView ? 5: 5,
+                    marginRight: mobileView ? 5: 5,
                     marginTop: sharedTopMargin,
                     marginBottom: sharedBottomMargin,
                     translateX: tileChartTranslateX1
                 });
 
-                const barChartWidth = chartDimensions.boundedWidth / 3
-                barChartTranslateX2 = mobileView ? tileChartWidth + 55 : tileChartTranslateX2 + tileChartWidth;
-                barChartTranslateX3 = mobileView ? tileChartWidth + 45 : tileChartTranslateX3 + tileChartWidth;
+                barChartTranslateX2 = mobileView ? tileChartWidth + 55 : tileChartTranslateX2 + tileChartWidth + chartGap;
+                barChartTranslateX3 = mobileView ? tileChartWidth + 45 : tileChartTranslateX3 + tileChartWidth + chartGap;
                 initBarChart({
                     width: barChartWidth,
                     height: chartHeight,
                     margin: defaultMargin,
-                    marginLeft: mobileView ? 20 : 60,
+                    marginLeft: mobileView ? 5 : 5,
+                    marginRight: mobileView ? 5 : 5,
                     marginTop: sharedTopMargin,
                     marginBottom: sharedBottomMargin,
                     translateX: barChartTranslateX2});
 
-                const scatterChartWidth = chartDimensions.boundedWidth / 3
-                scatterChartTranslateX3 = mobileView ? tileChartWidth + barChartWidth + 50 : tileChartTranslateX3 + tileChartWidth + barChartWidth;
+                scatterChartTranslateX3 = mobileView ? tileChartWidth + barChartWidth + 50 : tileChartTranslateX3 + tileChartWidth + barChartWidth + chartGap * 2;
                 initScatterChart({
                     width: scatterChartWidth,
                     height: chartHeight,
                     margin: defaultMargin,
-                    marginLeft: mobileView ? 20 : 10,
-                    marginRight: scatterChartWidth / 3,
+                    marginLeft: mobileView ? 5 : 5,
+                    marginRight: mobileView ? 5 : 5,
                     marginTop: sharedTopMargin,
                     marginBottom: sharedBottomMargin,
                     translateX: scatterChartTranslateX3});
@@ -174,8 +181,8 @@
                 // draw charts, hiding bar and scatter charts to start
                 drawTileChart(tileData.value);
                 drawBarChart(barData.value);
-                barChartBounds
-                    .attr("display", "none");
+                barChartWrapper
+                    .attr("visibility", "hidden");
                 maskingRect = chartSVG
                     .append("rect")
                     .attr("id", "masking-rect")
@@ -184,11 +191,11 @@
                     .attr("width", barChartDimensions.width + scatterChartDimensions.width)
                     .attr("height", chartHeight)
                     .style("transform", `translate(${
-                        tileChartTranslateX1 + tileChartDimensions.width
+                        tileChartTranslateX1 + tileChartDimensions.width + chartGap * 0.5
                     }px, 0px)`);
                 drawScatterChart(scatterData.value);
-                scatterChartBounds
-                    .attr("display", "none");
+                scatterChartWrapper
+                    .attr("visibility", "hidden");
 
                 // add legends
                 addTileLegend()
@@ -305,10 +312,18 @@
             boundedHeight: height - marginTop - marginBottom
         }
 
-        tileChartBounds = chartBounds.append("g")
-            .attr("id", "tile-chart-bounds")
+        tileChartWrapper = chartBounds.append("g")
+            .attr("id", "tile-chart-wrapper")
             .style("transform", `translate(${
                 translateX
+            }px, ${
+                0
+            }px)`);
+
+        tileChartBounds = tileChartWrapper.append("g")
+            .attr("id", "tile-chart-bounds")
+            .style("transform", `translate(${
+                tileChartDimensions.margin.left
             }px, ${
                 tileChartDimensions.margin.top
             }px)`);
@@ -353,10 +368,18 @@
             boundedHeight: height - marginTop - marginBottom
         }
 
-        barChartBounds = chartBounds.append("g")
+        barChartWrapper = chartBounds.append("g")
+            .attr("id", "bar-chart-wrapper")
+            .style("transform", `translate(${
+                translateX
+            }px, ${
+                0
+            }px)`);
+
+        barChartBounds = barChartWrapper.append("g")
             .attr("id", "bar-chart-bounds")
             .style("transform", `translate(${
-                translateX + barChartDimensions.margin.left
+                barChartDimensions.margin.left
             }px, ${
                 barChartDimensions.margin.top
             }px)`);
@@ -404,10 +427,18 @@
             boundedHeight: height - marginTop - marginBottom
         }
 
-        scatterChartBounds = chartBounds.append("g")
+        scatterChartWrapper = chartBounds.append("g")
+            .attr("id", "scatter-chart-wrapper")
+            .style("transform", `translate(${
+                translateX
+            }px, ${
+                0
+            }px)`);
+
+        scatterChartBounds = scatterChartWrapper.append("g")
             .attr("id", "scatter-chart-bounds")
             .style("transform", `translate(${
-                translateX + scatterChartDimensions.margin.left
+                scatterChartDimensions.margin.left
             }px, ${
                 scatterChartDimensions.margin.top
             }px)`);
@@ -676,7 +707,7 @@
             {
                 tickFormat: ".0f", 
                 customSuffix: 'cm', 
-                tickSize: -chartDimensions.boundedWidth - tileChartTranslateX1 - tileChartDimensions.margin.left, 
+                tickSize: -(chartDimensions.boundedWidth - tileChartDimensions.margin.left), 
                 nticks: 20,
                 keepDomain: false
             }
@@ -1055,7 +1086,7 @@
                 .append("circle") // append a rectangle for each element
                     .attr("class", "point")
                     .attr("id", d => 'point-' + identifierAccessor(d))
-                    .attr("cx", d => scatterXScale(xAccessor(d)))
+                    .attr("cx", d => scatterXScale(xAccessor(d)) + scatterXScale.bandwidth()/2)
                     .attr("cy", d => barYScale(yAccessor(d)) + barYScale.bandwidth()/2)
                     .attr("r", mobileView ? 2 : desktopPointSize)
                     .style("fill", d => scatterColorScale(colorAccessor(d)));
@@ -1075,7 +1106,7 @@
             .attr("dy", 0)
             .attr("text-anchor", "middle")
             .attr("dominant-baseline", "text-before-edge")
-            .attr("text-width", scatterChartDimensions.boundedWidth + (scatterChartDimensions.margin.left + scatterChartDimensions.margin.right) / 2)
+            .attr("text-width", scatterChartDimensions.boundedWidth)
             .text('Burned vegetation type')
             .call(d => wrap(d))
 
@@ -1187,77 +1218,74 @@
             })
     }
 
-    function showChart(hiddenChartBounds, translateX, translateY) {
-        hiddenChartBounds
+    function showChart(hiddenChart, translateX) {
+        hiddenChart
             .attr("opacity", 0)
-            .attr("display", "auto")
+            .attr("visibility", "visible")
+            // .attr("display", "auto")
             .transition()
             .duration(transitionLength)
             .attr("opacity", 1)
             .style("transform", `translate(${
                 translateX
-            }px, ${
-                translateY
-            }px)`);
+            }px, 0px)`);
     }
 
-    function hideChart(visibleChartBounds) {
-        visibleChartBounds
+    function hideChart(visibleChart) {
+        visibleChart
             .transition()
             .duration(transitionLength)
             .attr("opacity", 0)
         setTimeout(function() {
-            visibleChartBounds
-                .attr("display", "none")
+            visibleChart
+                .attr("visibility", "hidden");
         }, transitionLength)
     }
 
-    function moveChart(visibleChartBounds, translateX, translateY) {
-        visibleChartBounds
+    function moveChart(visibleChart, translateX) {
+        visibleChart
             .transition()
             .duration(transitionLength)
             .style("transform", `translate(${
                 translateX
-            }px, ${
-                translateY
-            }px)`);
+            }px, 0px)`);
     }
 
     function updateChartView(index) {
-        const barChartHidden = barChartBounds.node().attributes.display.value == 'none';
-        const scatterChartHidden = scatterChartBounds.node().attributes.display.value == 'none';
+        const barChartHidden = barChartWrapper.node().attributes.visibility.value == 'hidden';
+        const scatterChartHidden = scatterChartWrapper.node().attributes.visibility.value == 'hidden';
         if (index == 1) {
             maskingRect
                 .transition()
                 .duration(transitionLength)
                 .delay(transitionLength / 4)
                 .style("transform", `translate(${
-                    tileChartTranslateX1 + tileChartDimensions.width
+                    tileChartTranslateX1 + tileChartDimensions.width + chartGap * 0.5
                 }px, 0px)`);
-            moveChart(tileChartBounds, tileChartTranslateX1, tileChartDimensions.margin.top)
-            if (!barChartHidden) hideChart(barChartBounds)            
+            moveChart(tileChartWrapper, tileChartTranslateX1)
+            if (!barChartHidden) hideChart(barChartWrapper)            
         } else if (index == 2) {
             maskingRect
                 .style("transform", `translate(${
-                    tileChartTranslateX2 + tileChartDimensions.width + barChartDimensions.width
+                    tileChartTranslateX2 + tileChartDimensions.width + barChartDimensions.width + chartGap * 1.5
                 }px, 0px)`)
                 .transition()
                 .duration(transitionLength)
                 .delay(transitionLength / 4)
                 .style("opacity", "1");
-            moveChart(tileChartBounds, tileChartTranslateX2, tileChartDimensions.margin.top)
+            moveChart(tileChartWrapper, tileChartTranslateX2)
             if (barChartHidden)  {
-                showChart(barChartBounds, barChartTranslateX2, barChartDimensions.margin.top)
+                showChart(barChartWrapper, barChartTranslateX2)
             } else {
-                moveChart(barChartBounds, barChartTranslateX2, barChartDimensions.margin.top)
+                moveChart(barChartWrapper, barChartTranslateX2)
             }
-            if (!scatterChartHidden) hideChart(scatterChartBounds) 
+            if (!scatterChartHidden) hideChart(scatterChartWrapper) 
         } else if (index == 3) {
             maskingRect
                 .style("opacity", "0")
-            moveChart(tileChartBounds, tileChartTranslateX3, tileChartDimensions.margin.top)
-            moveChart(barChartBounds, barChartTranslateX3, barChartDimensions.margin.top)
-            showChart(scatterChartBounds, scatterChartTranslateX3, scatterChartDimensions.margin.top)
+            moveChart(tileChartWrapper, tileChartTranslateX3)
+            moveChart(barChartWrapper, barChartTranslateX3)
+            showChart(scatterChartWrapper, scatterChartTranslateX3)
         }
     }
 
@@ -1278,14 +1306,21 @@
             dx = parseFloat(text.attr("dx")),
             tspan = text.text(null).append("tspan").attr("y", y).attr("dy", dy + "em");
 
+            
+            console.log(`wrap: ${words}`)
             while ((word = words.pop())) {
             line.push(word);
             tspan.text(line.join(" "));
+                console.log(`width: ${width}`)
+                console.log(`word: ${word}`)
+                console.log(tspan.node())
+                console.log(tspan.node().getComputedTextLength())
+                console.log(tspan.node().getComputedTextLength() > width)
                 if (tspan.node().getComputedTextLength() > width) {
-                line.pop();
-                tspan.text(line.join(" "));
-                line = [word];
-                tspan = text.append("tspan").attr("x", x).attr("y", y).attr("dx", dx).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
+                    line.pop();
+                    tspan.text(line.join(" "));
+                    line = [word];
+                    tspan = text.append("tspan").attr("x", x).attr("y", y).attr("dx", dx).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
                 }
             }
 
-- 
GitLab


From 804eb0c2ffcd3a9285e71e00c555611d35c3a16e Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Tue, 29 Oct 2024 14:18:02 -0500
Subject: [PATCH 36/38] center charts more

---
 src/components/WildfireAerosolsViz.vue | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/components/WildfireAerosolsViz.vue b/src/components/WildfireAerosolsViz.vue
index 621e2ca..2ce062d 100644
--- a/src/components/WildfireAerosolsViz.vue
+++ b/src/components/WildfireAerosolsViz.vue
@@ -129,21 +129,21 @@
                 initChart({
                     width: chartWidth,
                     height: chartHeight,
-                    margin: 30
+                    margin: mobileView ? 5 : 30
                 })
 
                 const defaultMargin = mobileView ? 5 : 10;
                 const sharedTopMargin = mobileView ? 135 : 130;
                 const sharedBottomMargin = mobileView ? 0 : 10;
 
-                chartGap = chartDimensions.boundedWidth / 11
+                chartGap = mobileView ? chartDimensions.boundedWidth / 9 : chartDimensions.boundedWidth / 11;
                 const tileChartWidth = mobileView ? chartGap * 3 : chartGap * 3;
                 const barChartWidth = mobileView ? chartGap * 3 : chartGap * 3;
                 const scatterChartWidth = mobileView ? chartGap * 2 : chartGap * 2;
 
                 tileChartTranslateX1 = mobileView ? (chartDimensions.boundedWidth - tileChartWidth) / 2: (chartDimensions.boundedWidth - tileChartWidth) / 2;
-                tileChartTranslateX2 = mobileView ? tileChartTranslateX1 - barChartWidth / 2 : tileChartTranslateX1 - barChartWidth / 2;
-                tileChartTranslateX3 = mobileView ? tileChartTranslateX2 - scatterChartWidth / 2 : tileChartTranslateX2 - scatterChartWidth / 2;
+                tileChartTranslateX2 = mobileView ? tileChartTranslateX1 - barChartWidth : tileChartTranslateX1 - barChartWidth / 1.75;
+                tileChartTranslateX3 = mobileView ? tileChartTranslateX2 - scatterChartWidth : tileChartTranslateX2 - scatterChartWidth;
                 initTileChart({
                     width: tileChartWidth,
                     height: chartHeight,
-- 
GitLab


From 184edb6f61fa1b4d65c2bfcb631851f4e6d3ccda Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Tue, 29 Oct 2024 14:28:52 -0500
Subject: [PATCH 37/38] clean up mobile translations

---
 src/components/WildfireAerosolsViz.vue | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/components/WildfireAerosolsViz.vue b/src/components/WildfireAerosolsViz.vue
index 2ce062d..3097331 100644
--- a/src/components/WildfireAerosolsViz.vue
+++ b/src/components/WildfireAerosolsViz.vue
@@ -136,14 +136,14 @@
                 const sharedTopMargin = mobileView ? 135 : 130;
                 const sharedBottomMargin = mobileView ? 0 : 10;
 
-                chartGap = mobileView ? chartDimensions.boundedWidth / 9 : chartDimensions.boundedWidth / 11;
+                chartGap = mobileView ? chartDimensions.boundedWidth / 11 : chartDimensions.boundedWidth / 11;
                 const tileChartWidth = mobileView ? chartGap * 3 : chartGap * 3;
                 const barChartWidth = mobileView ? chartGap * 3 : chartGap * 3;
                 const scatterChartWidth = mobileView ? chartGap * 2 : chartGap * 2;
 
                 tileChartTranslateX1 = mobileView ? (chartDimensions.boundedWidth - tileChartWidth) / 2: (chartDimensions.boundedWidth - tileChartWidth) / 2;
-                tileChartTranslateX2 = mobileView ? tileChartTranslateX1 - barChartWidth : tileChartTranslateX1 - barChartWidth / 1.75;
-                tileChartTranslateX3 = mobileView ? tileChartTranslateX2 - scatterChartWidth : tileChartTranslateX2 - scatterChartWidth;
+                tileChartTranslateX2 = mobileView ? tileChartTranslateX1 - barChartWidth / 1.75 : tileChartTranslateX1 - barChartWidth / 1.75;
+                tileChartTranslateX3 = mobileView ? tileChartTranslateX2 - scatterChartWidth + chartGap : tileChartTranslateX2 - scatterChartWidth;
                 initTileChart({
                     width: tileChartWidth,
                     height: chartHeight,
@@ -155,8 +155,8 @@
                     translateX: tileChartTranslateX1
                 });
 
-                barChartTranslateX2 = mobileView ? tileChartWidth + 55 : tileChartTranslateX2 + tileChartWidth + chartGap;
-                barChartTranslateX3 = mobileView ? tileChartWidth + 45 : tileChartTranslateX3 + tileChartWidth + chartGap;
+                barChartTranslateX2 = mobileView ? tileChartTranslateX2 + tileChartWidth + chartGap : tileChartTranslateX2 + tileChartWidth + chartGap;
+                barChartTranslateX3 = mobileView ? tileChartTranslateX3 + tileChartWidth + chartGap : tileChartTranslateX3 + tileChartWidth + chartGap;
                 initBarChart({
                     width: barChartWidth,
                     height: chartHeight,
@@ -167,7 +167,7 @@
                     marginBottom: sharedBottomMargin,
                     translateX: barChartTranslateX2});
 
-                scatterChartTranslateX3 = mobileView ? tileChartWidth + barChartWidth + 50 : tileChartTranslateX3 + tileChartWidth + barChartWidth + chartGap * 2;
+                scatterChartTranslateX3 = mobileView ? tileChartTranslateX3 + tileChartWidth + barChartWidth + chartGap * 1.5 : tileChartTranslateX3 + tileChartWidth + barChartWidth + chartGap * 2;
                 initScatterChart({
                     width: scatterChartWidth,
                     height: chartHeight,
@@ -1260,14 +1260,14 @@
                 .duration(transitionLength)
                 .delay(transitionLength / 4)
                 .style("transform", `translate(${
-                    tileChartTranslateX1 + tileChartDimensions.width + chartGap * 0.5
+                    tileChartTranslateX1 + tileChartDimensions.width + chartGap
                 }px, 0px)`);
             moveChart(tileChartWrapper, tileChartTranslateX1)
             if (!barChartHidden) hideChart(barChartWrapper)            
         } else if (index == 2) {
             maskingRect
                 .style("transform", `translate(${
-                    tileChartTranslateX2 + tileChartDimensions.width + barChartDimensions.width + chartGap * 1.5
+                    tileChartTranslateX2 + tileChartDimensions.width + barChartDimensions.width + chartGap * 2
                 }px, 0px)`)
                 .transition()
                 .duration(transitionLength)
-- 
GitLab


From 2242d07140861571f1c826c6d3247cc5ba5c4b3b Mon Sep 17 00:00:00 2001
From: Corson-Dosch <hcorson-dosch@usgs.gov>
Date: Tue, 29 Oct 2024 14:29:28 -0500
Subject: [PATCH 38/38] fix comment

---
 src/components/WildfireAerosolsViz.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/components/WildfireAerosolsViz.vue b/src/components/WildfireAerosolsViz.vue
index 3097331..380b883 100644
--- a/src/components/WildfireAerosolsViz.vue
+++ b/src/components/WildfireAerosolsViz.vue
@@ -122,7 +122,7 @@
             if (tileData.value.length > 0 && barData.value.length > 0) {
                 
                 // initialize chart elements
-                // on desktop, don't let chart height exceed 1200px
+                // on desktop, don't let chart height exceed 800px
                 const desktopHeight = window.innerHeight < 770 ? window.innerHeight * 0.85 : Math.min(window.innerHeight * 0.75, 800);
                 chartHeight = mobileView ? window.innerHeight * 0.6 : desktopHeight;
                 chartWidth = chart.value.offsetWidth;
-- 
GitLab