From 10551148bf0bfcbe7f3eaf4a995e22144e8eee48 Mon Sep 17 00:00:00 2001 From: Corson-Dosch <hcorson-dosch@usgs.gov> Date: Fri, 16 Aug 2024 16:04:35 -0500 Subject: [PATCH 01/25] draft fish clmate vulnerability chart --- public/fish_as_food_climate_test.csv | 13 + src/assets/content/ChartGrid.js | 8 +- src/components/FishAsFoodLinkChartViz.vue | 344 ++++++++++++++++++++++ src/components/Viz3Viz.vue | 37 --- 4 files changed, 361 insertions(+), 41 deletions(-) create mode 100644 public/fish_as_food_climate_test.csv create mode 100644 src/components/FishAsFoodLinkChartViz.vue delete mode 100644 src/components/Viz3Viz.vue diff --git a/public/fish_as_food_climate_test.csv b/public/fish_as_food_climate_test.csv new file mode 100644 index 0000000..e052fb1 --- /dev/null +++ b/public/fish_as_food_climate_test.csv @@ -0,0 +1,13 @@ +family,thermal_guild,species,cvi_2030,cvi_2075 +A,warm,species_1,0.319953022,0.35962678 +A,warm,species_2,0.224863212,0.194521316 +A,warm,species_3,0.835617044,0.810848349 +A,warm,species_4,0.68844979,0.080709824 +A,warm,species_5,0.506342706,0.733569142 +A,warm,species_6,0.313221865,0.058278019 +B,cool,species_7,0.484686402,0.646526582 +B,cool,species_8,0.943640492,0.319394434 +B,cool,species_9,0.894768952,0.914064253 +B,cool,species_10,0.069633857,0.332752341 +C,cold,species_11,0.953508638,0.178160176 +C,cold,species_12,0.092467137,0.585798787 diff --git a/src/assets/content/ChartGrid.js b/src/assets/content/ChartGrid.js index 251d23a..61ff2c4 100644 --- a/src/assets/content/ChartGrid.js +++ b/src/assets/content/ChartGrid.js @@ -21,10 +21,10 @@ export default { description: 'Description or key takeaways from this Viz (Viz 2)' }, { - title: 'Viz 3', - project: 'Findex', - vizKey: 'Viz3', - vizRoute: 'viz-3', + title: 'Climate vulnerability of recreationally fished inland fish species', + project: 'Fish as Food', + vizKey: 'FishAsFoodLinkChart', + vizRoute: 'inland-rec-fish-climate', img_src: 'Placeholder_thumbnail.PNG', alt: '', description: 'Description or key takeaways from this Viz (Viz 3)' diff --git a/src/components/FishAsFoodLinkChartViz.vue b/src/components/FishAsFoodLinkChartViz.vue new file mode 100644 index 0000000..6321669 --- /dev/null +++ b/src/components/FishAsFoodLinkChartViz.vue @@ -0,0 +1,344 @@ +<template> + <!---VizSection--> + <VizSection + :figures="true" + :fig-caption="false" + > + <!-- HEADING --> + <template #heading> + <h2> + </h2> + </template> + <!-- FIGURES --> + <template #aboveExplanation> + </template> + <template #figures> + <div id="chart-container"></div> + </template> + <!-- FIGURE CAPTION --> + <template #figureCaption> + </template> + <!-- EXPLANATION --> + <template #belowExplanation> + </template> + </VizSection> +</template> + +<script setup> + import { onMounted, ref } from "vue"; + import * as d3 from 'd3'; + import VizSection from '@/components/VizSection.vue'; + + // define props + defineProps({ + text: { type: Object } + }) + + // global variables + const publicPath = import.meta.env.BASE_URL; + const dataFile = 'fish_as_food_climate_test.csv' + const data = ref(); + let chartDimensions; + const chartTitle = 'Title of chart'; + let chartBounds; + let xScale; + let yScale; + let widthScale; + let colorScale; + + + // Behavior on mounted (functions called here) + // Load data and then make chart + onMounted(async () => { + try { + await loadDatasets(); + if (data.value.length > 0) { + initChart({ + width: 400, + height: 300, + margin: 10, + marginBottom: 30, + marginLeft: 50}); + drawChart(); + } 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); + return data; + } catch (error) { + console.error(`Error loading data from ${fileName}`, error); + return []; + } + } + + function initChart({ + width = 500, // outer width, in pixels + height = width, // 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 + chartDimensions = { + width, + height, + margin: { + top: marginTop, + right: marginRight, + bottom: marginBottom, + left: marginLeft + } + } + chartDimensions.boundedWidth = chartDimensions.width - chartDimensions.margin.left - chartDimensions.margin.right + chartDimensions.boundedHeight = chartDimensions.height - chartDimensions.margin.top - chartDimensions.margin.bottom + + // 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", "chart-svg") + + // 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() + } + + function initXScale() { + // scale for the x axis (domain set in `drawChart()`) + xScale = d3.scaleLinear() + .range([0, chartDimensions.boundedWidth]); + + // add group for x axis + const 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 + + // generate x axis + xAxis + .call(d3.axisBottom(xScale).tickSize(0).tickPadding(10)) + // .select(".domain").remove() // remove axis line + + // add placeholder for x axis title (title text set in drawChart()) + xAxis + .append("text") + .attr("class", "x-axis axis-title") + .attr("x", -chartDimensions.boundedWidth / 2) + .style("text-anchor", "middle") + .attr("role", "presentation") + .attr("aria-hidden", true) + } + + function initYScale() { + // scale for y axis (domain set in `drawChart()`) + yScale = d3.scaleBand() + .range([chartDimensions.boundedHeight, 0]) + .padding(0.1) + + // add group for y axis + const yAxis = chartBounds.append("g") + .attr("id", "y-axis") + .attr("class", "axis") + .attr("aria-hidden", true) + + // generate y axis + yAxis + .call(d3.axisLeft(yScale).tickSize(2)) + // .select(".domain").remove() // remove axis line + + // add placeholder for y axis title (title text set in drawChart()) + yAxis + .append("text") + .attr("class", "y-axis axis-title") + .attr("x", -chartDimensions.boundedHeight / 2) + .attr("transform", "rotate(-90)") + .style("text-anchor", "middle") + .attr("role", "presentation") + .attr("aria-hidden", true) + } + + function initWidthScale(low, high) { + widthScale = d3.scaleLinear() + .domain([0, 1]) + .range([low, high]); + } + + function initColorScale(data) { + colorScale = d3.scaleOrdinal() + .domain(data) + .range(data.map(item => getColor(item))); + } + + function getColor(item) { + let itemColor; + switch(item) { + case 'warm': + itemColor = '#FF7256'; + break; + case 'cool': + itemColor = '#5CACEE'; + break; + case 'cold': + itemColor = '#36648B'; + break; + default: + itemColor = '#B3B3B3'; + } + return itemColor; + } + + function drawChart() { + // accessor functions + const yAccessor = d => d.species + const xAccessor = d => d.cvi + const x0Accessor = d => d.cvi_2030 + const x1Accessor = d => d.cvi_2075 + const widthAccessor = d => d.position + const colorAccessor = d => d.thermal_guild + const identifierAccessor = d => d.family + '_' + d.species + + // set domain for xScale + xScale + .domain([0, d3.max([d3.max(data.value, x0Accessor), d3.max(data.value, x1Accessor)])]) + + // set domain for yScale + yScale + .domain(d3.union(data.value.map(yAccessor).sort(d3.ascending))) + + // set up width scale + const radiusPosition0 = 1 + const strokeRatio = 0.3 + const strokeWidth1 = strokeRatio * yScale.bandwidth() / 2 + const radiusPosition1 = (1 - strokeRatio) * yScale.bandwidth() / 2 + initWidthScale(radiusPosition0, radiusPosition1) + + // set up area function + const area = d3.area() + .x(d => xScale(xAccessor(d))) + .y0(d => yScale(yAccessor(d)) - widthScale(widthAccessor(d))) + .y1(d => yScale(yAccessor(d)) + widthScale(widthAccessor(d))); + + // set up color scale + const colorCategories = Array.from(new Set(data.value.map(colorAccessor))) + initColorScale(colorCategories) + + // set up area data + const areaCategories = Array.from(new Set(data.value.map(yAccessor))) + const areaData = areaCategories.map(areaCategory => { + return [ + { + species: areaCategory, + cvi: data.value.filter(d => d.species === areaCategory)[0].cvi_2030, + position: 0, + thermal_guild: data.value.filter(d => d.species === areaCategory)[0].thermal_guild + }, + { + species: areaCategory, + cvi: data.value.filter(d => d.species === areaCategory)[0].cvi_2075, + position: 1, + thermal_guild: data.value.filter(d => d.species === areaCategory)[0].thermal_guild + } + ] + }) + + // draw chart + chartBounds.append("g") + .attr("id", "areas") + .selectAll('.area') + .data(areaData) + .enter() + .append('path') + .attr("id", d => 'area-2030-' + identifierAccessor(d)) + .attr('class', "area") + .attr('d', d => area(d)) + .attr('fill', d => colorScale(colorAccessor(d[0]))) + .style("opacity", 1); + + chartBounds.append("g") + .attr("id", "points-2030") + .attr("class", "points points_2030") + .selectAll('points') + .data(data.value) + .enter() + .append("circle") + .attr("id", d => 'point-2030-' + identifierAccessor(d)) + .attr("class", "point") + .attr("cx", d => xScale(x0Accessor(d))) + .attr("cy", d => yScale(yAccessor(d))) + .attr("r", radiusPosition0) + .style("stroke", d => colorScale(colorAccessor(d))) + .style("fill", "white") + + chartBounds.append("g") + .attr("id", "points-2075") + .attr("class", "points points_2075") + .selectAll('points') + .data(data.value) + .enter() + .append("circle") + .attr("id", d => 'point-2075-' + identifierAccessor(d)) + .attr("class", "point") + .attr("cx", d => xScale(x1Accessor(d))) + .attr("cy", d => yScale(yAccessor(d))) + .attr("r", radiusPosition1 - strokeWidth1 / 2) + .style("stroke", d => colorScale(colorAccessor(d))) + .style("stroke-width", strokeWidth1) + .style("fill", "white") + + // chartBounds.append("g") + // .attr("id", "eyes-2075") + // .attr("class", "eyes eyes_2075") + // .selectAll('eyes') + // .data(data.value) + // .enter() + // .append("circle") + // .attr("id", d => 'point-2075-' + identifierAccessor(d)) + // .attr("class", "point") + // .attr("cx", d => { + // return x1Accessor(d) > x0Accessor(d) ? (xScale(x1Accessor(d)) + radiusPosition1 / 4) : (xScale(x1Accessor(d)) - radiusPosition1 / 4) + // }) + // .attr("cy", d => yScale(yAccessor(d)) + radiusPosition1 / 4) + // .attr("r", radiusPosition1 / 2) + // .style("stroke", 'none') + // .style("fill", "black") + + } +</script> + +<style> +</style> \ No newline at end of file diff --git a/src/components/Viz3Viz.vue b/src/components/Viz3Viz.vue deleted file mode 100644 index 5f149d4..0000000 --- a/src/components/Viz3Viz.vue +++ /dev/null @@ -1,37 +0,0 @@ -<template> - <!---VizSection--> - <VizSection - :figures="true" - :fig-caption="false" - > - <!-- HEADING --> - <template #heading> - <h2> - This is the Viz 3 viz! - </h2> - </template> - <!-- FIGURES --> - <template #aboveExplanation> - </template> - <template #figures> - </template> - <!-- FIGURE CAPTION --> - <template #figureCaption> - </template> - <!-- EXPLANATION --> - <template #belowExplanation> - </template> - </VizSection> -</template> - -<script setup> - import VizSection from '@/components/VizSection.vue'; - - // define props - defineProps({ - text: { type: Object } - }) -</script> - -<style> -</style> \ No newline at end of file -- GitLab From 1a7b8a945cfbc71ee95969fbed2a23ca22f07003 Mon Sep 17 00:00:00 2001 From: Corson-Dosch <hcorson-dosch@usgs.gov> Date: Wed, 21 Aug 2024 00:16:14 -0500 Subject: [PATCH 02/25] udpate data --- public/fish_as_food_climate.csv | 84 ++++++++++++++++++++++++++++ public/fish_as_food_climate_test.csv | 26 ++++----- 2 files changed, 97 insertions(+), 13 deletions(-) create mode 100644 public/fish_as_food_climate.csv diff --git a/public/fish_as_food_climate.csv b/public/fish_as_food_climate.csv new file mode 100644 index 0000000..74ed7c5 --- /dev/null +++ b/public/fish_as_food_climate.csv @@ -0,0 +1,84 @@ +family,thermal_guild,species,cvi_2030,cvi_2075,cvi_2030_family,cvi_2075_family +Cyprinidae,warm,Albanian roach,0.10778174260000001,0.0941208422,0.10778174260000001,0.0941208422 +Cyprinidae,warm,Asp,0.013429605127960001,0.011727456938119999,0.013429605127960001,0.011727456938119999 +Cyprinidae,warm,Barbel,0.04705801130333334,0.039834021549,0.04705801130333334,0.039834021549 +Cyprinidae,warm,Bata,0.0055121400746,0.007563008347000001,0.0055121400746,0.007563008347000001 +Cyprinidae,warm,BeyÅŸehir bleak,7.267071073e-4,6.011377922000001e-4,7.267071073e-4,6.011377922000001e-4 +Cyprinidae,warm,Black bream,0.051246241799999995,0.061345053,0.051246241799999995,0.061345053 +Cyprinidae,warm,Black carp,0.051246241799999995,0.061345053,0.051246241799999995,0.061345053 +Cyprinidae,warm,Blue-finned mahseer,0.0224598681704,0.027820540914799997,0.0224598681704,0.027820540914799997 +Cyprinidae,warm,Catla,0.0220743179623,0.0302873745485,0.0220743179623,0.0302873745485 +Cyprinidae,warm,Chocolate mahseer,0.0028306322192,0.0034302133044,0.0028306322192,0.0034302133044 +Cyprinidae,warm,Chub,0.03216167369029999,0.026581131487899996,0.03216167369029999,0.026581131487899996 +Cyprinidae,warm,Common barbel,0.002834382148,0.0023992692516,0.002834382148,0.0023992692516 +Cyprinidae,warm,Common bleak,0.00854949538,0.007072209320000001,0.00854949538,0.007072209320000001 +Cyprinidae,warm,Common bream,0.04600620713676522,0.03642865846998261,0.04600620713676522,0.03642865846998261 +Cyprinidae,warm,Common carp,0.13811330662322036,0.1199294950838376,0.13811330662322036,0.1199294950838376 +Cyprinidae,warm,Common nase,0.026387573782000004,0.023642661213400005,0.026387573782000004,0.023642661213400005 +Cyprinidae,warm,Common roach,0.05312624687117,0.042622199202855,0.05312624687117,0.042622199202855 +Cyprinidae,warm,Crucian carp,0.116377215108,0.10105514804045,0.116377215108,0.10105514804045 +Cyprinidae,warm,Cyprinds,0.032788926016,0.028471980271733335,0.032788926016,0.028471980271733335 +Cyprinidae,warm,European carp,0.13040639766,0.11323726735275001,0.13040639766,0.11323726735275001 +Cyprinidae,warm,European chub,0.001917238372,0.001584568196,0.001917238372,0.001584568196 +Cyprinidae,warm,Ghonia,0.0018287941369,0.0025092223955,0.0018287941369,0.0025092223955 +Cyprinidae,warm,Golden mahseer,0.0034355641156000004,0.0039133067584,0.0034355641156000004,0.0039133067584 +Cyprinidae,warm,Goldfish,0.189583548,0.16462323395,0.189583548,0.16462323395 +Cyprinidae,warm,Grass carp,0.028623161305374997,0.034263768144375,0.028623161305374997,0.034263768144375 +Cyprinidae,warm,Gudgeon,0.0344069672688,0.030376054976399995,0.0344069672688,0.030376054976399995 +Cyprinidae,warm,Italian bleak,0.10233745969859999,0.0846543455604,0.10233745969859999,0.0846543455604 +Cyprinidae,warm,Kalibaus,0.0036833459377,0.0050537859515,0.0036833459377,0.0050537859515 +Cyprinidae,warm,Kutum,2.15702238e-4,1.9326426059999998e-4,2.15702238e-4,1.9326426059999998e-4 +Cyprinidae,warm,Labeo,0.017643999771499996,0.024208694942499998,0.017643999771499996,0.024208694942499998 +Cyprinidae,warm,Mrigal,0.019859158866899998,0.0272480347455,0.019859158866899998,0.0272480347455 +Cyprinidae,warm,Mullet,0.022048241277999998,0.018222534254,0.022048241277999998,0.018222534254 +Cyprinidae,warm,Orange River mudfish,0.01803036473,0.024738812350000004,0.01803036473,0.024738812350000004 +Cyprinidae,warm,Pale chub,0.0023231629616,0.0027809757360000003,0.0023231629616,0.0027809757360000003 +Cyprinidae,warm,Pearl mullet,0.1777012614733,0.14699587071620002,0.1777012614733,0.14699587071620002 +Cyprinidae,warm,Prussian carp,0.064146947634,0.055701447087225,0.064146947634,0.055701447087225 +Cyprinidae,warm,Prussian/Crucian carp,0.0216666912,0.018814083880000002,0.0216666912,0.018814083880000002 +Cyprinidae,warm,Razor fish (ziege),3.7723609910000005e-4,3.294229477e-4,3.7723609910000005e-4,3.294229477e-4 +Cyprinidae,warm,Red garra,0.017643999771499996,0.024208694942499998,0.017643999771499996,0.024208694942499998 +Cyprinidae,warm,Rohu,0.0017515211452,0.0024031989140000003,0.0017515211452,0.0024031989140000003 +Cyprinidae,warm,Rudd,0.010652671243479999,0.00881197281272,0.010652671243479999,0.00881197281272 +Cyprinidae,warm,Rui,0.0368077017131,0.0505025183545,0.0368077017131,0.0505025183545 +Cyprinidae,warm,Shabout,0.0011991616780000001,0.0010150754526,0.0011991616780000001,0.0010150754526 +Cyprinidae,warm,Silver bream,0.010815032339999999,0.008563564440000001,0.010815032339999999,0.008563564440000001 +Cyprinidae,warm,Silver carp,0.029425103982114283,0.035223745194000004,0.029425103982114283,0.035223745194000004 +Cyprinidae,warm,Tench,0.017184651140000003,0.015171401295,0.017184651140000003,0.015171401295 +Cyprinidae,warm,Transcau casian barb,0.014553462183,0.0123193248111,0.014553462183,0.0123193248111 +Cyprinidae,warm,Trout barb,0.004578617316,0.0038757426371999995,0.004578617316,0.0038757426371999995 +Cyprinidae,warm,Vimba bream,0.0212868941635,0.0185888663345,0.0212868941635,0.0185888663345 +Cyprinidae,warm,White Bream,0.043260129359999996,0.034254257760000004,0.043260129359999996,0.034254257760000004 +Percidae,cool,Eurasian Ruffe,0.00486753292,0.0043199098100000005,0.00486753292,0.0043199098100000005 +Percidae,cool,European perch,0.07259463927767063,0.06347059881833862,0.07259463927767063,0.06347059881833862 +Percidae,cool,Golden perch,0.0334276893426,0.0344360226346,0.0334276893426,0.0344360226346 +Percidae,cool,Redfin perch,0.0263136745872,0.0230064464784,0.0263136745872,0.0230064464784 +Percidae,cool,Volga pikeperch,8.01681972e-4,6.487112373e-4,8.01681972e-4,6.487112373e-4 +Percidae,cool,Walleye,0.10268783482319999,0.0983561899176,0.10268783482319999,0.0983561899176 +Percidae,cool,Yellow perch,0.0488076651,0.0456058989,0.0488076651,0.0456058989 +Percidae,cool,Zander (pikeperch),0.037962706268244786,0.032533836325300367,0.037962706268244786,0.032533836325300367 +Salmonidae,cold,Arctic Char,0.0356557719054,0.03500071147903334,0.0356557719054,0.03500071147903334 +Salmonidae,cold,Atlantic salmon,0.265177841604771,0.24011746662753772,0.265177841604771,0.24011746662753772 +Salmonidae,cold,Balkan trout,0.026710147320000002,0.023695444160000002,0.026710147320000002,0.023695444160000002 +Salmonidae,cold,Brook Trout,0.026461932224000003,0.024708687848000004,0.026461932224000003,0.024708687848000004 +Salmonidae,cold,Brown Trout,0.050732109690129576,0.04500611164287132,0.050732109690129576,0.04500611164287132 +Salmonidae,cold,Cherry trout,0.0029371095160000004,0.0027625745076,0.0029371095160000004,0.0027625745076 +Salmonidae,cold,Chinook Salmon (Quinnat Salmon),0.1600752957,0.15222991949999998,0.1600752957,0.15222991949999998 +Salmonidae,cold,Chum salmon,0.02885045315,0.0284171047,0.02885045315,0.0284171047 +Salmonidae,cold,Coho salmon,0.2150947458,0.20066481688,0.2150947458,0.20066481688 +Salmonidae,cold,Common huchen,5.120283490000001e-5,4.81393951e-5,5.120283490000001e-5,4.81393951e-5 +Salmonidae,cold,Dolly varden/Char,0.0034788601644,0.0033581483528,0.0034788601644,0.0033581483528 +Salmonidae,cold,European grayling,0.024065881522700002,0.0212009485031,0.024065881522700002,0.0212009485031 +Salmonidae,cold,Pink salmon,0.025550719900000005,0.02443988385,0.025550719900000005,0.02443988385 +Salmonidae,cold,Rainbow trout,0.06302419030074105,0.05967045472608766,0.06302419030074105,0.05967045472608766 +Salmonidae,cold,Salmon,0.06449020412644443,0.06255334611855555,0.06449020412644443,0.06255334611855555 +Salmonidae,cold,Sockeye salmon,0.029446763100000002,0.029350874649999997,0.029446763100000002,0.029350874649999997 +Salmonidae,cold,Steelhead,0.0049289009400000005,0.00466661704,0.0049289009400000005,0.00466661704 +Salmonidae,cold,Trout,0.0858357697255,0.08267910955158334,0.0858357697255,0.08267910955158334 +Salmonidae,cold,Vendace,0.04116622401,0.0383635935,0.04116622401,0.0383635935 +Salmonidae,cold,Whitefish,0.05007684609903145,0.04666757308221,0.05007684609903145,0.04666757308221 +Siluridae,warm,Boal,0.0124330956288,0.0105118070912,0.0124330956288,0.0105118070912 +Siluridae,warm,Catfish,0.033215868223999995,0.028083014042666665,0.033215868223999995,0.028083014042666665 +Siluridae,warm,European catfish,0.016684117296,0.014105917603999998,0.016684117296,0.014105917603999998 +Siluridae,warm,Far eastern catfish,0.0031082739072,0.0026279517728,0.0031082739072,0.0026279517728 +Siluridae,warm,Wels catfish,0.0333433019136,0.028190755380945457,0.0333433019136,0.028190755380945457 diff --git a/public/fish_as_food_climate_test.csv b/public/fish_as_food_climate_test.csv index e052fb1..d6846eb 100644 --- a/public/fish_as_food_climate_test.csv +++ b/public/fish_as_food_climate_test.csv @@ -1,13 +1,13 @@ -family,thermal_guild,species,cvi_2030,cvi_2075 -A,warm,species_1,0.319953022,0.35962678 -A,warm,species_2,0.224863212,0.194521316 -A,warm,species_3,0.835617044,0.810848349 -A,warm,species_4,0.68844979,0.080709824 -A,warm,species_5,0.506342706,0.733569142 -A,warm,species_6,0.313221865,0.058278019 -B,cool,species_7,0.484686402,0.646526582 -B,cool,species_8,0.943640492,0.319394434 -B,cool,species_9,0.894768952,0.914064253 -B,cool,species_10,0.069633857,0.332752341 -C,cold,species_11,0.953508638,0.178160176 -C,cold,species_12,0.092467137,0.585798787 +family,thermal_guild,species,cvi_2030,cvi_2075,cvi_2030_family,cvi_2075_family +A,warm,species_1,0.319953022,0.35962678,0.85151318,0.020097514 +A,warm,species_2,0.224863212,0.194521316,0.85151318,0.020097514 +A,warm,species_3,0.835617044,0.810848349,0.85151318,0.020097514 +A,warm,species_4,0.68844979,0.080709824,0.85151318,0.020097514 +A,warm,species_5,0.506342706,0.733569142,0.85151318,0.020097514 +A,warm,species_6,0.313221865,0.058278019,0.85151318,0.020097514 +B,cool,species_7,0.484686402,0.646526582,0.053519735,0.775921332 +B,cool,species_8,0.943640492,0.319394434,0.053519735,0.775921332 +B,cool,species_9,0.894768952,0.914064253,0.053519735,0.775921332 +B,cool,species_10,0.069633857,0.332752341,0.053519735,0.775921332 +C,cold,species_11,0.953508638,0.178160176,0.700810585,0.0658746 +C,cold,species_12,0.092467137,0.585798787,0.700810585,0.0658746 -- GitLab From 4a79278ebb6ebf7c66968fa9bfef4d5d100701d1 Mon Sep 17 00:00:00 2001 From: Corson-Dosch <hcorson-dosch@usgs.gov> Date: Wed, 21 Aug 2024 00:16:32 -0500 Subject: [PATCH 03/25] update chart styling, explore layout options --- src/components/FishAsFoodLinkChartViz.vue | 171 ++++++++++++++++++---- 1 file changed, 139 insertions(+), 32 deletions(-) diff --git a/src/components/FishAsFoodLinkChartViz.vue b/src/components/FishAsFoodLinkChartViz.vue index 6321669..642a0dc 100644 --- a/src/components/FishAsFoodLinkChartViz.vue +++ b/src/components/FishAsFoodLinkChartViz.vue @@ -6,14 +6,12 @@ > <!-- HEADING --> <template #heading> - <h2> - </h2> </template> <!-- FIGURES --> <template #aboveExplanation> </template> <template #figures> - <div id="chart-container"></div> + <div id="chart-container" class="maxWidth" ref="chart"></div> </template> <!-- FIGURE CAPTION --> <template #figureCaption> @@ -25,7 +23,7 @@ </template> <script setup> - import { onMounted, ref } from "vue"; + import { onMounted, ref, reactive } from "vue"; import * as d3 from 'd3'; import VizSection from '@/components/VizSection.vue'; @@ -36,15 +34,22 @@ // global variables const publicPath = import.meta.env.BASE_URL; - const dataFile = 'fish_as_food_climate_test.csv' + const dataFile = 'fish_as_food_climate.csv' //'fish_as_food_climate_test.csv' const data = ref(); + const chart = ref(null); + // const families = ref(); let chartDimensions; const chartTitle = 'Title of chart'; + let chartSVG; let chartBounds; let xScale; + let xAxis; let yScale; + let yAxis; let widthScale; let colorScale; + // Create a reactive object to track expanded families + // const expandedFamilies = reactive({}); // Behavior on mounted (functions called here) @@ -53,13 +58,20 @@ try { await loadDatasets(); if (data.value.length > 0) { + // families.value = Array.from(new Set(data.value.map(d => d.family))); + // // Initialize the expandedFamilies object + // families.value.forEach(family => { + // expandedFamilies[family] = false; + // }); + // expandedFamilies["A"] = !expandedFamilies["A"] + // console.log(expandedFamilies) initChart({ - width: 400, - height: 300, - margin: 10, + width: chart.value.offsetWidth, + height: 1600, + margin: 20, marginBottom: 30, - marginLeft: 50}); - drawChart(); + marginLeft: 200}); + drawChart(data.value); } else { console.error('Error loading data'); } @@ -111,7 +123,7 @@ chartDimensions.boundedHeight = chartDimensions.height - chartDimensions.margin.top - chartDimensions.margin.bottom // 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%") @@ -135,6 +147,9 @@ // Initialize scales initXScale() initYScale() + + // Inititalize gradients + initGradients() } function initXScale() { @@ -143,15 +158,15 @@ .range([0, chartDimensions.boundedWidth]); // add group for x axis - const xAxis = chartBounds.append("g") + 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 // generate x axis - xAxis - .call(d3.axisBottom(xScale).tickSize(0).tickPadding(10)) + // xAxis + // .call(d3.axisBottom(xScale).tickSize(0).tickPadding(10)) // .select(".domain").remove() // remove axis line // add placeholder for x axis title (title text set in drawChart()) @@ -171,14 +186,14 @@ .padding(0.1) // add group for y axis - const yAxis = chartBounds.append("g") + yAxis = chartBounds.append("g") .attr("id", "y-axis") .attr("class", "axis") .attr("aria-hidden", true) // generate y axis - yAxis - .call(d3.axisLeft(yScale).tickSize(2)) + // yAxis + // .call(d3.axisLeft(yScale).tickSize(2)) // .select(".domain").remove() // remove axis line // add placeholder for y axis title (title text set in drawChart()) @@ -192,6 +207,43 @@ .attr("aria-hidden", true) } + function initGradients() { + let colors = {warm: '#FF7256', cool: '#5CACEE', cold: '#36648B'} + Object.keys(colors).forEach(color => { + let lg_decreasing = chartSVG.append("defs").append("linearGradient") + .attr("id", color + "_gradient_decreasing") + .attr("x1", "0%") + .attr("x2", "100%") + .attr("y1", "0%") + .attr("y2", "0%"); + lg_decreasing.append("stop") + .attr("offset", "40%") + .style("stop-color", colors[color]) + .style("stop-opacity", 1) + + lg_decreasing.append("stop") + .attr("offset", "100%") + .style("stop-color", colors[color]) + .style("stop-opacity", 0) + + let lg_increasing = chartSVG.append("defs").append("linearGradient") + .attr("id", color + "_gradient_increasing") + .attr("x1", "100%") + .attr("x2", "0%") + .attr("y1", "0%") + .attr("y2", "0%"); + lg_increasing.append("stop") + .attr("offset", "40%") + .style("stop-color", colors[color]) + .style("stop-opacity", 1) + + lg_increasing.append("stop") + .attr("offset", "100%") + .style("stop-color", colors[color]) + .style("stop-opacity", 0) + }) + } + function initWidthScale(low, high) { widthScale = d3.scaleLinear() .domain([0, 1]) @@ -222,23 +274,49 @@ return itemColor; } - function drawChart() { + function drawChart(data) { // accessor functions const yAccessor = d => d.species + // const yAccessor_family = d => d.family const xAccessor = d => d.cvi + // const xAccessor_family = d => d.cvi_family const x0Accessor = d => d.cvi_2030 const x1Accessor = d => d.cvi_2075 + const x0Accessor_family = d => d.cvi_2030_family + const x1Accessor_family = d => d.cvi_2075_family const widthAccessor = d => d.position const colorAccessor = d => d.thermal_guild const identifierAccessor = d => d.family + '_' + d.species + // to get dynamic + // need key for data + // need enter update exit pattern with transitions + // need to transition chart height, yscale, yaxis + + // set domain for xScale xScale - .domain([0, d3.max([d3.max(data.value, x0Accessor), d3.max(data.value, x1Accessor)])]) + .domain([0, 0.3]) // d3.max([d3.max(data, x0Accessor), d3.max(data, x1Accessor)])]) + xAxis + .call(d3.axisBottom(xScale).tickSize(0).tickPadding(10)) + xAxis + .selectAll("text") + .attr("class", "axis-text") // set domain for yScale + // let yDomain = [] + // families.value.map(family => expandedFamilies[family] ? yDomain.push(data.filter(d => d.family === family).map(d => d.species)) : yDomain.push(family)) + // yDomain = yDomain.flat() + // yScale + // .domain(yDomain) yScale - .domain(d3.union(data.value.map(yAccessor).sort(d3.ascending))) + .domain(d3.union(data.map(yAccessor))) //.sort(d3.ascending))) + yAxis + .call(d3.axisLeft(yScale).tickSize(2)) + + yAxis + .selectAll("text") + .attr("class", "axis-text") // set up width scale const radiusPosition0 = 1 @@ -252,26 +330,35 @@ .x(d => xScale(xAccessor(d))) .y0(d => yScale(yAccessor(d)) - widthScale(widthAccessor(d))) .y1(d => yScale(yAccessor(d)) + widthScale(widthAccessor(d))); + // .x(d => expandedFamilies[d.family] ? xScale(xAccessor(d)) : xScale(xAccessor_family(d))) + // .y0(d => expandedFamilies[d.family] ? yScale(yAccessor(d)) - widthScale(widthAccessor(d)) : yScale(yAccessor_family(d)) - widthScale(widthAccessor(d))) + // .y1(d => expandedFamilies[d.family] ? yScale(yAccessor(d)) + widthScale(widthAccessor(d)) : yScale(yAccessor_family(d)) + widthScale(widthAccessor(d))); // set up color scale - const colorCategories = Array.from(new Set(data.value.map(colorAccessor))) + const colorCategories = Array.from(new Set(data.map(colorAccessor))) initColorScale(colorCategories) // set up area data - const areaCategories = Array.from(new Set(data.value.map(yAccessor))) + const areaCategories = Array.from(new Set(data.map(yAccessor))) const areaData = areaCategories.map(areaCategory => { return [ { species: areaCategory, - cvi: data.value.filter(d => d.species === areaCategory)[0].cvi_2030, + family: data.filter(d => d.species === areaCategory)[0].family, + cvi: x0Accessor(data.filter(d => d.species === areaCategory)[0]), + cvi_decreasing: x0Accessor(data.filter(d => d.species === areaCategory)[0]) > x1Accessor(data.filter(d => d.species === areaCategory)[0]), + cvi_family: x0Accessor_family(data.filter(d => d.species === areaCategory)[0]), position: 0, - thermal_guild: data.value.filter(d => d.species === areaCategory)[0].thermal_guild + thermal_guild: colorAccessor(data.filter(d => d.species === areaCategory)[0]) }, { species: areaCategory, - cvi: data.value.filter(d => d.species === areaCategory)[0].cvi_2075, + family: data.filter(d => d.species === areaCategory)[0].family, + cvi: x1Accessor(data.filter(d => d.species === areaCategory)[0]), + cvi_decreasing: x0Accessor(data.filter(d => d.species === areaCategory)[0]) > x1Accessor(data.filter(d => d.species === areaCategory)[0]), + cvi_family: x1Accessor_family(data.filter(d => d.species === areaCategory)[0]), position: 1, - thermal_guild: data.value.filter(d => d.species === areaCategory)[0].thermal_guild + thermal_guild: colorAccessor(data.filter(d => d.species === areaCategory)[0]) } ] }) @@ -286,14 +373,29 @@ .attr("id", d => 'area-2030-' + identifierAccessor(d)) .attr('class', "area") .attr('d', d => area(d)) - .attr('fill', d => colorScale(colorAccessor(d[0]))) - .style("opacity", 1); + .attr('fill', d => d[0].cvi_decreasing ? `url(#${d[0].thermal_guild}_gradient_decreasing)`: `url(#${d[0].thermal_guild}_gradient_increasing)`)//d => colorScale(colorAccessor(d[0]))) + .style("opacity", 1) + // .on("click", function(event, d) { + // const clickedFamily = d[0].family + // expandedFamilies[clickedFamily] = !expandedFamilies[clickedFamily] + // let yDomain = [] + // families.value.map(family => expandedFamilies[family] ? yDomain.push(data.filter(d => d.family === family).map(d => d.species)) : yDomain.push(family)) + // yDomain = yDomain.flat() + // const yBandwidth = yScale.bandwidth() + // chartDimensions.boundedHeight = yBandwidth * yDomain.length; + // chartDimensions.height = chartDimensions.boundedHeight + chartDimensions.margin.top + chartDimensions.margin.bottom; + // chartSVG.attr("viewBox", [0, 0, (chartDimensions.width), (chartDimensions.height)].join(' ')) + // yScale.range([chartDimensions.boundedHeight * 2, 0]) + // yAxis + // .call(d3.axisLeft(yScale).tickSize(2)) + // drawChart(data) + // }); chartBounds.append("g") .attr("id", "points-2030") .attr("class", "points points_2030") .selectAll('points') - .data(data.value) + .data(data) .enter() .append("circle") .attr("id", d => 'point-2030-' + identifierAccessor(d)) @@ -308,7 +410,7 @@ .attr("id", "points-2075") .attr("class", "points points_2075") .selectAll('points') - .data(data.value) + .data(data) .enter() .append("circle") .attr("id", d => 'point-2075-' + identifierAccessor(d)) @@ -324,7 +426,7 @@ // .attr("id", "eyes-2075") // .attr("class", "eyes eyes_2075") // .selectAll('eyes') - // .data(data.value) + // .data(data) // .enter() // .append("circle") // .attr("id", d => 'point-2075-' + identifierAccessor(d)) @@ -340,5 +442,10 @@ } </script> -<style> +<style lang="scss"> + .axis-text { + font-size: 1.8rem; + font-family: var(--default-font); + user-select: none; + } </style> \ No newline at end of file -- GitLab From 528e2461ecc566813505967efafaab744e95a272 Mon Sep 17 00:00:00 2001 From: Corson-Dosch <hcorson-dosch@usgs.gov> Date: Wed, 21 Aug 2024 14:20:01 -0500 Subject: [PATCH 04/25] switch to percent change and add axis labels --- public/fish_as_food_climate.csv | 166 +++++++++++----------- src/components/FishAsFoodLinkChartViz.vue | 99 +++++++++---- 2 files changed, 157 insertions(+), 108 deletions(-) diff --git a/public/fish_as_food_climate.csv b/public/fish_as_food_climate.csv index 74ed7c5..667f577 100644 --- a/public/fish_as_food_climate.csv +++ b/public/fish_as_food_climate.csv @@ -1,84 +1,84 @@ family,thermal_guild,species,cvi_2030,cvi_2075,cvi_2030_family,cvi_2075_family -Cyprinidae,warm,Albanian roach,0.10778174260000001,0.0941208422,0.10778174260000001,0.0941208422 -Cyprinidae,warm,Asp,0.013429605127960001,0.011727456938119999,0.013429605127960001,0.011727456938119999 -Cyprinidae,warm,Barbel,0.04705801130333334,0.039834021549,0.04705801130333334,0.039834021549 -Cyprinidae,warm,Bata,0.0055121400746,0.007563008347000001,0.0055121400746,0.007563008347000001 -Cyprinidae,warm,BeyÅŸehir bleak,7.267071073e-4,6.011377922000001e-4,7.267071073e-4,6.011377922000001e-4 -Cyprinidae,warm,Black bream,0.051246241799999995,0.061345053,0.051246241799999995,0.061345053 -Cyprinidae,warm,Black carp,0.051246241799999995,0.061345053,0.051246241799999995,0.061345053 -Cyprinidae,warm,Blue-finned mahseer,0.0224598681704,0.027820540914799997,0.0224598681704,0.027820540914799997 -Cyprinidae,warm,Catla,0.0220743179623,0.0302873745485,0.0220743179623,0.0302873745485 -Cyprinidae,warm,Chocolate mahseer,0.0028306322192,0.0034302133044,0.0028306322192,0.0034302133044 -Cyprinidae,warm,Chub,0.03216167369029999,0.026581131487899996,0.03216167369029999,0.026581131487899996 -Cyprinidae,warm,Common barbel,0.002834382148,0.0023992692516,0.002834382148,0.0023992692516 -Cyprinidae,warm,Common bleak,0.00854949538,0.007072209320000001,0.00854949538,0.007072209320000001 -Cyprinidae,warm,Common bream,0.04600620713676522,0.03642865846998261,0.04600620713676522,0.03642865846998261 -Cyprinidae,warm,Common carp,0.13811330662322036,0.1199294950838376,0.13811330662322036,0.1199294950838376 -Cyprinidae,warm,Common nase,0.026387573782000004,0.023642661213400005,0.026387573782000004,0.023642661213400005 -Cyprinidae,warm,Common roach,0.05312624687117,0.042622199202855,0.05312624687117,0.042622199202855 -Cyprinidae,warm,Crucian carp,0.116377215108,0.10105514804045,0.116377215108,0.10105514804045 -Cyprinidae,warm,Cyprinds,0.032788926016,0.028471980271733335,0.032788926016,0.028471980271733335 -Cyprinidae,warm,European carp,0.13040639766,0.11323726735275001,0.13040639766,0.11323726735275001 -Cyprinidae,warm,European chub,0.001917238372,0.001584568196,0.001917238372,0.001584568196 -Cyprinidae,warm,Ghonia,0.0018287941369,0.0025092223955,0.0018287941369,0.0025092223955 -Cyprinidae,warm,Golden mahseer,0.0034355641156000004,0.0039133067584,0.0034355641156000004,0.0039133067584 -Cyprinidae,warm,Goldfish,0.189583548,0.16462323395,0.189583548,0.16462323395 -Cyprinidae,warm,Grass carp,0.028623161305374997,0.034263768144375,0.028623161305374997,0.034263768144375 -Cyprinidae,warm,Gudgeon,0.0344069672688,0.030376054976399995,0.0344069672688,0.030376054976399995 -Cyprinidae,warm,Italian bleak,0.10233745969859999,0.0846543455604,0.10233745969859999,0.0846543455604 -Cyprinidae,warm,Kalibaus,0.0036833459377,0.0050537859515,0.0036833459377,0.0050537859515 -Cyprinidae,warm,Kutum,2.15702238e-4,1.9326426059999998e-4,2.15702238e-4,1.9326426059999998e-4 -Cyprinidae,warm,Labeo,0.017643999771499996,0.024208694942499998,0.017643999771499996,0.024208694942499998 -Cyprinidae,warm,Mrigal,0.019859158866899998,0.0272480347455,0.019859158866899998,0.0272480347455 -Cyprinidae,warm,Mullet,0.022048241277999998,0.018222534254,0.022048241277999998,0.018222534254 -Cyprinidae,warm,Orange River mudfish,0.01803036473,0.024738812350000004,0.01803036473,0.024738812350000004 -Cyprinidae,warm,Pale chub,0.0023231629616,0.0027809757360000003,0.0023231629616,0.0027809757360000003 -Cyprinidae,warm,Pearl mullet,0.1777012614733,0.14699587071620002,0.1777012614733,0.14699587071620002 -Cyprinidae,warm,Prussian carp,0.064146947634,0.055701447087225,0.064146947634,0.055701447087225 -Cyprinidae,warm,Prussian/Crucian carp,0.0216666912,0.018814083880000002,0.0216666912,0.018814083880000002 -Cyprinidae,warm,Razor fish (ziege),3.7723609910000005e-4,3.294229477e-4,3.7723609910000005e-4,3.294229477e-4 -Cyprinidae,warm,Red garra,0.017643999771499996,0.024208694942499998,0.017643999771499996,0.024208694942499998 -Cyprinidae,warm,Rohu,0.0017515211452,0.0024031989140000003,0.0017515211452,0.0024031989140000003 -Cyprinidae,warm,Rudd,0.010652671243479999,0.00881197281272,0.010652671243479999,0.00881197281272 -Cyprinidae,warm,Rui,0.0368077017131,0.0505025183545,0.0368077017131,0.0505025183545 -Cyprinidae,warm,Shabout,0.0011991616780000001,0.0010150754526,0.0011991616780000001,0.0010150754526 -Cyprinidae,warm,Silver bream,0.010815032339999999,0.008563564440000001,0.010815032339999999,0.008563564440000001 -Cyprinidae,warm,Silver carp,0.029425103982114283,0.035223745194000004,0.029425103982114283,0.035223745194000004 -Cyprinidae,warm,Tench,0.017184651140000003,0.015171401295,0.017184651140000003,0.015171401295 -Cyprinidae,warm,Transcau casian barb,0.014553462183,0.0123193248111,0.014553462183,0.0123193248111 -Cyprinidae,warm,Trout barb,0.004578617316,0.0038757426371999995,0.004578617316,0.0038757426371999995 -Cyprinidae,warm,Vimba bream,0.0212868941635,0.0185888663345,0.0212868941635,0.0185888663345 -Cyprinidae,warm,White Bream,0.043260129359999996,0.034254257760000004,0.043260129359999996,0.034254257760000004 -Percidae,cool,Eurasian Ruffe,0.00486753292,0.0043199098100000005,0.00486753292,0.0043199098100000005 -Percidae,cool,European perch,0.07259463927767063,0.06347059881833862,0.07259463927767063,0.06347059881833862 -Percidae,cool,Golden perch,0.0334276893426,0.0344360226346,0.0334276893426,0.0344360226346 -Percidae,cool,Redfin perch,0.0263136745872,0.0230064464784,0.0263136745872,0.0230064464784 -Percidae,cool,Volga pikeperch,8.01681972e-4,6.487112373e-4,8.01681972e-4,6.487112373e-4 -Percidae,cool,Walleye,0.10268783482319999,0.0983561899176,0.10268783482319999,0.0983561899176 -Percidae,cool,Yellow perch,0.0488076651,0.0456058989,0.0488076651,0.0456058989 -Percidae,cool,Zander (pikeperch),0.037962706268244786,0.032533836325300367,0.037962706268244786,0.032533836325300367 -Salmonidae,cold,Arctic Char,0.0356557719054,0.03500071147903334,0.0356557719054,0.03500071147903334 -Salmonidae,cold,Atlantic salmon,0.265177841604771,0.24011746662753772,0.265177841604771,0.24011746662753772 -Salmonidae,cold,Balkan trout,0.026710147320000002,0.023695444160000002,0.026710147320000002,0.023695444160000002 -Salmonidae,cold,Brook Trout,0.026461932224000003,0.024708687848000004,0.026461932224000003,0.024708687848000004 -Salmonidae,cold,Brown Trout,0.050732109690129576,0.04500611164287132,0.050732109690129576,0.04500611164287132 -Salmonidae,cold,Cherry trout,0.0029371095160000004,0.0027625745076,0.0029371095160000004,0.0027625745076 -Salmonidae,cold,Chinook Salmon (Quinnat Salmon),0.1600752957,0.15222991949999998,0.1600752957,0.15222991949999998 -Salmonidae,cold,Chum salmon,0.02885045315,0.0284171047,0.02885045315,0.0284171047 -Salmonidae,cold,Coho salmon,0.2150947458,0.20066481688,0.2150947458,0.20066481688 -Salmonidae,cold,Common huchen,5.120283490000001e-5,4.81393951e-5,5.120283490000001e-5,4.81393951e-5 -Salmonidae,cold,Dolly varden/Char,0.0034788601644,0.0033581483528,0.0034788601644,0.0033581483528 -Salmonidae,cold,European grayling,0.024065881522700002,0.0212009485031,0.024065881522700002,0.0212009485031 -Salmonidae,cold,Pink salmon,0.025550719900000005,0.02443988385,0.025550719900000005,0.02443988385 -Salmonidae,cold,Rainbow trout,0.06302419030074105,0.05967045472608766,0.06302419030074105,0.05967045472608766 -Salmonidae,cold,Salmon,0.06449020412644443,0.06255334611855555,0.06449020412644443,0.06255334611855555 -Salmonidae,cold,Sockeye salmon,0.029446763100000002,0.029350874649999997,0.029446763100000002,0.029350874649999997 -Salmonidae,cold,Steelhead,0.0049289009400000005,0.00466661704,0.0049289009400000005,0.00466661704 -Salmonidae,cold,Trout,0.0858357697255,0.08267910955158334,0.0858357697255,0.08267910955158334 -Salmonidae,cold,Vendace,0.04116622401,0.0383635935,0.04116622401,0.0383635935 -Salmonidae,cold,Whitefish,0.05007684609903145,0.04666757308221,0.05007684609903145,0.04666757308221 -Siluridae,warm,Boal,0.0124330956288,0.0105118070912,0.0124330956288,0.0105118070912 -Siluridae,warm,Catfish,0.033215868223999995,0.028083014042666665,0.033215868223999995,0.028083014042666665 -Siluridae,warm,European catfish,0.016684117296,0.014105917603999998,0.016684117296,0.014105917603999998 -Siluridae,warm,Far eastern catfish,0.0031082739072,0.0026279517728,0.0031082739072,0.0026279517728 -Siluridae,warm,Wels catfish,0.0333433019136,0.028190755380945457,0.0333433019136,0.028190755380945457 +Cyprinidae,warm,Albanian roach,0,-0.12674595966311653,0,-0.12674595966311653 +Cyprinidae,warm,Asp,0,-0.12674595966311658,0,-0.12674595966311658 +Cyprinidae,warm,Barbel,0,-0.15351243187409472,0,-0.15351243187409472 +Cyprinidae,warm,Bata,0,0.37206388891501935,0,0.37206388891501935 +Cyprinidae,warm,BeyÅŸehir bleak,0,-0.1727921934966878,0,-0.1727921934966878 +Cyprinidae,warm,Black bream,0,0.19706442551266273,0,0.19706442551266273 +Cyprinidae,warm,Black carp,0,0.19706442551266273,0,0.19706442551266273 +Cyprinidae,warm,Blue-finned mahseer,0,0.2386778365629439,0,0.2386778365629439 +Cyprinidae,warm,Catla,0,0.3720638889150193,0,0.3720638889150193 +Cyprinidae,warm,Chocolate mahseer,0,0.21181878773691584,0,0.21181878773691584 +Cyprinidae,warm,Chub,0,-0.17351529202546123,0,-0.17351529202546123 +Cyprinidae,warm,Common barbel,0,-0.15351243187409475,0,-0.15351243187409475 +Cyprinidae,warm,Common bleak,0,-0.1727921934966878,0,-0.1727921934966878 +Cyprinidae,warm,Common bream,0,-0.20817948843969888,0,-0.20817948843969888 +Cyprinidae,warm,Common carp,0,-0.13165865030651286,0,-0.13165865030651286 +Cyprinidae,warm,Common nase,0,-0.10402292348955595,0,-0.10402292348955595 +Cyprinidae,warm,Common roach,0,-0.19771860966928614,0,-0.19771860966928614 +Cyprinidae,warm,Crucian carp,0,-0.1316586503065129,0,-0.1316586503065129 +Cyprinidae,warm,Cyprinds,0,-0.13165865030651291,0,-0.13165865030651291 +Cyprinidae,warm,European carp,0,-0.13165865030651283,0,-0.13165865030651283 +Cyprinidae,warm,European chub,0,-0.17351529202546126,0,-0.17351529202546126 +Cyprinidae,warm,Ghonia,0,0.3720638889150193,0,0.3720638889150193 +Cyprinidae,warm,Golden mahseer,0,0.13905799068941688,0,0.13905799068941688 +Cyprinidae,warm,Goldfish,0,-0.13165865030651283,0,-0.13165865030651283 +Cyprinidae,warm,Grass carp,0,0.1970644255126627,0,0.1970644255126627 +Cyprinidae,warm,Gudgeon,0,-0.11715395492165936,0,-0.11715395492165936 +Cyprinidae,warm,Italian bleak,0,-0.17279219349668792,0,-0.17279219349668792 +Cyprinidae,warm,Kalibaus,0,0.3720638889150193,0,0.3720638889150193 +Cyprinidae,warm,Kutum,0,-0.10402292348955605,0,-0.10402292348955605 +Cyprinidae,warm,Labeo,0,0.37206388891501935,0,0.37206388891501935 +Cyprinidae,warm,Mrigal,0,0.3720638889150193,0,0.3720638889150193 +Cyprinidae,warm,Mullet,0,-0.1735152920254612,0,-0.1735152920254612 +Cyprinidae,warm,Orange River mudfish,0,0.3720638889150193,0,0.3720638889150193 +Cyprinidae,warm,Pale chub,0,0.19706442551266287,0,0.19706442551266287 +Cyprinidae,warm,Pearl mullet,0,-0.17279219349668795,0,-0.17279219349668795 +Cyprinidae,warm,Prussian carp,0,-0.13165865030651291,0,-0.13165865030651291 +Cyprinidae,warm,Prussian/Crucian carp,0,-0.13165865030651283,0,-0.13165865030651283 +Cyprinidae,warm,Razor fish (ziege),0,-0.1267459596631165,0,-0.1267459596631165 +Cyprinidae,warm,Red garra,0,0.37206388891501935,0,0.37206388891501935 +Cyprinidae,warm,Rohu,0,0.37206388891501935,0,0.37206388891501935 +Cyprinidae,warm,Rudd,0,-0.1727921934966879,0,-0.1727921934966879 +Cyprinidae,warm,Rui,0,0.37206388891501924,0,0.37206388891501924 +Cyprinidae,warm,Shabout,0,-0.15351243187409466,0,-0.15351243187409466 +Cyprinidae,warm,Silver bream,0,-0.20817948843969877,0,-0.20817948843969877 +Cyprinidae,warm,Silver carp,0,0.1970644255126629,0,0.1970644255126629 +Cyprinidae,warm,Tench,0,-0.11715395492165945,0,-0.11715395492165945 +Cyprinidae,warm,Transcau casian barb,0,-0.1535124318740947,0,-0.1535124318740947 +Cyprinidae,warm,Trout barb,0,-0.1535124318740947,0,-0.1535124318740947 +Cyprinidae,warm,Vimba bream,0,-0.12674595966311641,0,-0.12674595966311641 +Cyprinidae,warm,White Bream,0,-0.20817948843969877,0,-0.20817948843969877 +Percidae,cool,Eurasian Ruffe,0,-0.11250527094534767,0,-0.11250527094534767 +Percidae,cool,European perch,0,-0.12568476887712102,0,-0.12568476887712102 +Percidae,cool,Golden perch,0,0.030164612386623692,0,0.030164612386623692 +Percidae,cool,Redfin perch,0,-0.1256847688771208,0,-0.1256847688771208 +Percidae,cool,Volga pikeperch,0,-0.19081224231396332,0,-0.19081224231396332 +Percidae,cool,Walleye,0,-0.04218264912350984,0,-0.04218264912350984 +Percidae,cool,Yellow perch,0,-0.06559965926335624,0,-0.06559965926335624 +Percidae,cool,Zander (pikeperch),0,-0.1430053459462026,0,-0.1430053459462026 +Salmonidae,cold,Arctic Char,0,-0.018371792037054607,0,-0.018371792037054607 +Salmonidae,cold,Atlantic salmon,0,-0.09450403105167436,0,-0.09450403105167436 +Salmonidae,cold,Balkan trout,0,-0.11286733554414553,0,-0.11286733554414553 +Salmonidae,cold,Brook Trout,0,-0.06625534224631832,0,-0.06625534224631832 +Salmonidae,cold,Brown Trout,0,-0.11286733554414562,0,-0.11286733554414562 +Salmonidae,cold,Cherry trout,0,-0.05942407235726659,0,-0.05942407235726659 +Salmonidae,cold,Chinook Salmon (Quinnat Salmon),0,-0.04901053698319066,0,-0.04901053698319066 +Salmonidae,cold,Chum salmon,0,-0.015020507572166128,0,-0.015020507572166128 +Salmonidae,cold,Coho salmon,0,-0.06708638496180307,0,-0.06708638496180307 +Salmonidae,cold,Common huchen,0,-0.05982949588597892,0,-0.05982949588597892 +Salmonidae,cold,Dolly varden/Char,0,-0.034698667349516474,0,-0.034698667349516474 +Salmonidae,cold,European grayling,0,-0.1190454219139104,0,-0.1190454219139104 +Salmonidae,cold,Pink salmon,0,-0.04347572414192542,0,-0.04347572414192542 +Salmonidae,cold,Rainbow trout,0,-0.05321346547492189,0,-0.05321346547492189 +Salmonidae,cold,Salmon,0,-0.030033367611790023,0,-0.030033367611790023 +Salmonidae,cold,Sockeye salmon,0,-0.00325633244218971,0,-0.00325633244218971 +Salmonidae,cold,Steelhead,0,-0.05321346547492191,0,-0.05321346547492191 +Salmonidae,cold,Trout,0,-0.036775579505042726,0,-0.036775579505042726 +Salmonidae,cold,Vendace,0,-0.06808082541938247,0,-0.06808082541938247 +Salmonidae,cold,Whitefish,0,-0.06808082541938261,0,-0.06808082541938261 +Siluridae,warm,Boal,0,-0.15453018258377504,0,-0.15453018258377504 +Siluridae,warm,Catfish,0,-0.154530182583775,0,-0.154530182583775 +Siluridae,warm,European catfish,0,-0.1545301825837752,0,-0.1545301825837752 +Siluridae,warm,Far eastern catfish,0,-0.15453018258377504,0,-0.15453018258377504 +Siluridae,warm,Wels catfish,0,-0.15453018258377502,0,-0.15453018258377502 diff --git a/src/components/FishAsFoodLinkChartViz.vue b/src/components/FishAsFoodLinkChartViz.vue index 642a0dc..aa884e6 100644 --- a/src/components/FishAsFoodLinkChartViz.vue +++ b/src/components/FishAsFoodLinkChartViz.vue @@ -23,7 +23,7 @@ </template> <script setup> - import { onMounted, ref, reactive } from "vue"; + import { onMounted, ref } from "vue"; //, reactive import * as d3 from 'd3'; import VizSection from '@/components/VizSection.vue'; @@ -67,9 +67,9 @@ // console.log(expandedFamilies) initChart({ width: chart.value.offsetWidth, - height: 1600, + height: 1800, margin: 20, - marginBottom: 30, + marginBottom: 60, marginLeft: 200}); drawChart(data.value); } else { @@ -169,14 +169,14 @@ // .call(d3.axisBottom(xScale).tickSize(0).tickPadding(10)) // .select(".domain").remove() // remove axis line - // add placeholder for x axis title (title text set in drawChart()) - xAxis - .append("text") - .attr("class", "x-axis axis-title") - .attr("x", -chartDimensions.boundedWidth / 2) - .style("text-anchor", "middle") - .attr("role", "presentation") - .attr("aria-hidden", true) + // // add placeholder for x axis title (title text set in drawChart()) + // xAxis + // .append("text") + // .attr("class", "x-axis axis-title") + // .attr("x", -chartDimensions.boundedWidth / 2) + // .style("text-anchor", "middle") + // .attr("role", "presentation") + // .attr("aria-hidden", true) } function initYScale() { @@ -196,15 +196,15 @@ // .call(d3.axisLeft(yScale).tickSize(2)) // .select(".domain").remove() // remove axis line - // add placeholder for y axis title (title text set in drawChart()) - yAxis - .append("text") - .attr("class", "y-axis axis-title") - .attr("x", -chartDimensions.boundedHeight / 2) - .attr("transform", "rotate(-90)") - .style("text-anchor", "middle") - .attr("role", "presentation") - .attr("aria-hidden", true) + // // add placeholder for y axis title (title text set in drawChart()) + // yAxis + // .append("text") + // .attr("class", "y-axis axis-title") + // .attr("x", -chartDimensions.boundedHeight / 2) + // .attr("transform", "rotate(-90)") + // .style("text-anchor", "middle") + // .attr("role", "presentation") + // .attr("aria-hidden", true) } function initGradients() { @@ -296,13 +296,45 @@ // set domain for xScale xScale - .domain([0, 0.3]) // d3.max([d3.max(data, x0Accessor), d3.max(data, x1Accessor)])]) + // .domain([0, 0.3]) // d3.max([d3.max(data, x0Accessor), d3.max(data, x1Accessor)])]) + .domain([-0.4, 0.4]) + console.log(xScale.domain()) + xAxis .call(d3.axisBottom(xScale).tickSize(0).tickPadding(10)) xAxis .selectAll("text") .attr("class", "axis-text") + + const xAxisLabelYPosition = xAxis.select("text").attr('y') + const xAxisLabelDy = xAxis.select("text").attr('dy') + xAxis.append("text") + .attr("class", "x-axis axis-title") + .attr("x", chartDimensions.boundedWidth / 2) + .attr("y", xAxisLabelYPosition * 4) + .attr("dy", xAxisLabelDy) + .style("text-anchor", "middle") + .text('Percent change in harvest-weighted climate vulnerability, 2030-2075') + // xAxis.append("text") + // .attr("class", "x-axis axis-title") + // .attr("x", 0) + // .attr("y", xAxisLabelYPosition * 4) + // .attr("dy", xAxisLabelDy) + // .style("text-anchor", "start") + // .text('Less vulnerable') + // xAxis.append("text") + // .attr("class", "x-axis axis-title") + // .attr("x", chartDimensions.boundedWidth) + // .attr("y", xAxisLabelYPosition * 4) + // .attr("dy", xAxisLabelDy) + // .style("text-anchor", "end") + // .text('More vulnerable') + + // Remove axix line and labels + // xAxis.select(".domain").remove() + // xAxis.call(d3.axisBottom(xScale).tickValues([])) + // set domain for yScale // let yDomain = [] // families.value.map(family => expandedFamilies[family] ? yDomain.push(data.filter(d => d.family === family).map(d => d.species)) : yDomain.push(family)) @@ -318,6 +350,16 @@ .selectAll("text") .attr("class", "axis-text") + const yAxisLabelXPosition = yAxis.select("text").attr('x') + const yAxisLabelDx = yAxis.select("text").attr('dx') + yAxis.append("text") + .attr("class", "y-axis axis-title") + .attr("y", 0) + .attr("x", yAxisLabelXPosition) + .attr("dx", yAxisLabelDx) + .style("text-anchor", "end") + .text('Species') + // set up width scale const radiusPosition0 = 1 const strokeRatio = 0.3 @@ -328,8 +370,8 @@ // set up area function const area = d3.area() .x(d => xScale(xAccessor(d))) - .y0(d => yScale(yAccessor(d)) - widthScale(widthAccessor(d))) - .y1(d => yScale(yAccessor(d)) + widthScale(widthAccessor(d))); + .y0(d => yScale(yAccessor(d)) + yScale.bandwidth() / 2 - widthScale(widthAccessor(d))) + .y1(d => yScale(yAccessor(d)) + yScale.bandwidth() / 2+ widthScale(widthAccessor(d))); // .x(d => expandedFamilies[d.family] ? xScale(xAccessor(d)) : xScale(xAccessor_family(d))) // .y0(d => expandedFamilies[d.family] ? yScale(yAccessor(d)) - widthScale(widthAccessor(d)) : yScale(yAccessor_family(d)) - widthScale(widthAccessor(d))) // .y1(d => expandedFamilies[d.family] ? yScale(yAccessor(d)) + widthScale(widthAccessor(d)) : yScale(yAccessor_family(d)) + widthScale(widthAccessor(d))); @@ -401,7 +443,7 @@ .attr("id", d => 'point-2030-' + identifierAccessor(d)) .attr("class", "point") .attr("cx", d => xScale(x0Accessor(d))) - .attr("cy", d => yScale(yAccessor(d))) + .attr("cy", d => yScale(yAccessor(d)) + yScale.bandwidth() / 2) .attr("r", radiusPosition0) .style("stroke", d => colorScale(colorAccessor(d))) .style("fill", "white") @@ -416,7 +458,7 @@ .attr("id", d => 'point-2075-' + identifierAccessor(d)) .attr("class", "point") .attr("cx", d => xScale(x1Accessor(d))) - .attr("cy", d => yScale(yAccessor(d))) + .attr("cy", d => yScale(yAccessor(d)) + yScale.bandwidth() / 2) .attr("r", radiusPosition1 - strokeWidth1 / 2) .style("stroke", d => colorScale(colorAccessor(d))) .style("stroke-width", strokeWidth1) @@ -448,4 +490,11 @@ 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 7e8348bd46d218a78c7f16c3905e93bf8de5a793 Mon Sep 17 00:00:00 2001 From: Corson-Dosch <hcorson-dosch@usgs.gov> Date: Tue, 27 Aug 2024 10:23:12 -0500 Subject: [PATCH 05/25] add ref to track if scale is percent change --- public/fish_as_food_climate.csv | 166 +++++++++++----------- src/components/FishAsFoodLinkChartViz.vue | 19 ++- 2 files changed, 95 insertions(+), 90 deletions(-) diff --git a/public/fish_as_food_climate.csv b/public/fish_as_food_climate.csv index 667f577..74ed7c5 100644 --- a/public/fish_as_food_climate.csv +++ b/public/fish_as_food_climate.csv @@ -1,84 +1,84 @@ family,thermal_guild,species,cvi_2030,cvi_2075,cvi_2030_family,cvi_2075_family -Cyprinidae,warm,Albanian roach,0,-0.12674595966311653,0,-0.12674595966311653 -Cyprinidae,warm,Asp,0,-0.12674595966311658,0,-0.12674595966311658 -Cyprinidae,warm,Barbel,0,-0.15351243187409472,0,-0.15351243187409472 -Cyprinidae,warm,Bata,0,0.37206388891501935,0,0.37206388891501935 -Cyprinidae,warm,BeyÅŸehir bleak,0,-0.1727921934966878,0,-0.1727921934966878 -Cyprinidae,warm,Black bream,0,0.19706442551266273,0,0.19706442551266273 -Cyprinidae,warm,Black carp,0,0.19706442551266273,0,0.19706442551266273 -Cyprinidae,warm,Blue-finned mahseer,0,0.2386778365629439,0,0.2386778365629439 -Cyprinidae,warm,Catla,0,0.3720638889150193,0,0.3720638889150193 -Cyprinidae,warm,Chocolate mahseer,0,0.21181878773691584,0,0.21181878773691584 -Cyprinidae,warm,Chub,0,-0.17351529202546123,0,-0.17351529202546123 -Cyprinidae,warm,Common barbel,0,-0.15351243187409475,0,-0.15351243187409475 -Cyprinidae,warm,Common bleak,0,-0.1727921934966878,0,-0.1727921934966878 -Cyprinidae,warm,Common bream,0,-0.20817948843969888,0,-0.20817948843969888 -Cyprinidae,warm,Common carp,0,-0.13165865030651286,0,-0.13165865030651286 -Cyprinidae,warm,Common nase,0,-0.10402292348955595,0,-0.10402292348955595 -Cyprinidae,warm,Common roach,0,-0.19771860966928614,0,-0.19771860966928614 -Cyprinidae,warm,Crucian carp,0,-0.1316586503065129,0,-0.1316586503065129 -Cyprinidae,warm,Cyprinds,0,-0.13165865030651291,0,-0.13165865030651291 -Cyprinidae,warm,European carp,0,-0.13165865030651283,0,-0.13165865030651283 -Cyprinidae,warm,European chub,0,-0.17351529202546126,0,-0.17351529202546126 -Cyprinidae,warm,Ghonia,0,0.3720638889150193,0,0.3720638889150193 -Cyprinidae,warm,Golden mahseer,0,0.13905799068941688,0,0.13905799068941688 -Cyprinidae,warm,Goldfish,0,-0.13165865030651283,0,-0.13165865030651283 -Cyprinidae,warm,Grass carp,0,0.1970644255126627,0,0.1970644255126627 -Cyprinidae,warm,Gudgeon,0,-0.11715395492165936,0,-0.11715395492165936 -Cyprinidae,warm,Italian bleak,0,-0.17279219349668792,0,-0.17279219349668792 -Cyprinidae,warm,Kalibaus,0,0.3720638889150193,0,0.3720638889150193 -Cyprinidae,warm,Kutum,0,-0.10402292348955605,0,-0.10402292348955605 -Cyprinidae,warm,Labeo,0,0.37206388891501935,0,0.37206388891501935 -Cyprinidae,warm,Mrigal,0,0.3720638889150193,0,0.3720638889150193 -Cyprinidae,warm,Mullet,0,-0.1735152920254612,0,-0.1735152920254612 -Cyprinidae,warm,Orange River mudfish,0,0.3720638889150193,0,0.3720638889150193 -Cyprinidae,warm,Pale chub,0,0.19706442551266287,0,0.19706442551266287 -Cyprinidae,warm,Pearl mullet,0,-0.17279219349668795,0,-0.17279219349668795 -Cyprinidae,warm,Prussian carp,0,-0.13165865030651291,0,-0.13165865030651291 -Cyprinidae,warm,Prussian/Crucian carp,0,-0.13165865030651283,0,-0.13165865030651283 -Cyprinidae,warm,Razor fish (ziege),0,-0.1267459596631165,0,-0.1267459596631165 -Cyprinidae,warm,Red garra,0,0.37206388891501935,0,0.37206388891501935 -Cyprinidae,warm,Rohu,0,0.37206388891501935,0,0.37206388891501935 -Cyprinidae,warm,Rudd,0,-0.1727921934966879,0,-0.1727921934966879 -Cyprinidae,warm,Rui,0,0.37206388891501924,0,0.37206388891501924 -Cyprinidae,warm,Shabout,0,-0.15351243187409466,0,-0.15351243187409466 -Cyprinidae,warm,Silver bream,0,-0.20817948843969877,0,-0.20817948843969877 -Cyprinidae,warm,Silver carp,0,0.1970644255126629,0,0.1970644255126629 -Cyprinidae,warm,Tench,0,-0.11715395492165945,0,-0.11715395492165945 -Cyprinidae,warm,Transcau casian barb,0,-0.1535124318740947,0,-0.1535124318740947 -Cyprinidae,warm,Trout barb,0,-0.1535124318740947,0,-0.1535124318740947 -Cyprinidae,warm,Vimba bream,0,-0.12674595966311641,0,-0.12674595966311641 -Cyprinidae,warm,White Bream,0,-0.20817948843969877,0,-0.20817948843969877 -Percidae,cool,Eurasian Ruffe,0,-0.11250527094534767,0,-0.11250527094534767 -Percidae,cool,European perch,0,-0.12568476887712102,0,-0.12568476887712102 -Percidae,cool,Golden perch,0,0.030164612386623692,0,0.030164612386623692 -Percidae,cool,Redfin perch,0,-0.1256847688771208,0,-0.1256847688771208 -Percidae,cool,Volga pikeperch,0,-0.19081224231396332,0,-0.19081224231396332 -Percidae,cool,Walleye,0,-0.04218264912350984,0,-0.04218264912350984 -Percidae,cool,Yellow perch,0,-0.06559965926335624,0,-0.06559965926335624 -Percidae,cool,Zander (pikeperch),0,-0.1430053459462026,0,-0.1430053459462026 -Salmonidae,cold,Arctic Char,0,-0.018371792037054607,0,-0.018371792037054607 -Salmonidae,cold,Atlantic salmon,0,-0.09450403105167436,0,-0.09450403105167436 -Salmonidae,cold,Balkan trout,0,-0.11286733554414553,0,-0.11286733554414553 -Salmonidae,cold,Brook Trout,0,-0.06625534224631832,0,-0.06625534224631832 -Salmonidae,cold,Brown Trout,0,-0.11286733554414562,0,-0.11286733554414562 -Salmonidae,cold,Cherry trout,0,-0.05942407235726659,0,-0.05942407235726659 -Salmonidae,cold,Chinook Salmon (Quinnat Salmon),0,-0.04901053698319066,0,-0.04901053698319066 -Salmonidae,cold,Chum salmon,0,-0.015020507572166128,0,-0.015020507572166128 -Salmonidae,cold,Coho salmon,0,-0.06708638496180307,0,-0.06708638496180307 -Salmonidae,cold,Common huchen,0,-0.05982949588597892,0,-0.05982949588597892 -Salmonidae,cold,Dolly varden/Char,0,-0.034698667349516474,0,-0.034698667349516474 -Salmonidae,cold,European grayling,0,-0.1190454219139104,0,-0.1190454219139104 -Salmonidae,cold,Pink salmon,0,-0.04347572414192542,0,-0.04347572414192542 -Salmonidae,cold,Rainbow trout,0,-0.05321346547492189,0,-0.05321346547492189 -Salmonidae,cold,Salmon,0,-0.030033367611790023,0,-0.030033367611790023 -Salmonidae,cold,Sockeye salmon,0,-0.00325633244218971,0,-0.00325633244218971 -Salmonidae,cold,Steelhead,0,-0.05321346547492191,0,-0.05321346547492191 -Salmonidae,cold,Trout,0,-0.036775579505042726,0,-0.036775579505042726 -Salmonidae,cold,Vendace,0,-0.06808082541938247,0,-0.06808082541938247 -Salmonidae,cold,Whitefish,0,-0.06808082541938261,0,-0.06808082541938261 -Siluridae,warm,Boal,0,-0.15453018258377504,0,-0.15453018258377504 -Siluridae,warm,Catfish,0,-0.154530182583775,0,-0.154530182583775 -Siluridae,warm,European catfish,0,-0.1545301825837752,0,-0.1545301825837752 -Siluridae,warm,Far eastern catfish,0,-0.15453018258377504,0,-0.15453018258377504 -Siluridae,warm,Wels catfish,0,-0.15453018258377502,0,-0.15453018258377502 +Cyprinidae,warm,Albanian roach,0.10778174260000001,0.0941208422,0.10778174260000001,0.0941208422 +Cyprinidae,warm,Asp,0.013429605127960001,0.011727456938119999,0.013429605127960001,0.011727456938119999 +Cyprinidae,warm,Barbel,0.04705801130333334,0.039834021549,0.04705801130333334,0.039834021549 +Cyprinidae,warm,Bata,0.0055121400746,0.007563008347000001,0.0055121400746,0.007563008347000001 +Cyprinidae,warm,BeyÅŸehir bleak,7.267071073e-4,6.011377922000001e-4,7.267071073e-4,6.011377922000001e-4 +Cyprinidae,warm,Black bream,0.051246241799999995,0.061345053,0.051246241799999995,0.061345053 +Cyprinidae,warm,Black carp,0.051246241799999995,0.061345053,0.051246241799999995,0.061345053 +Cyprinidae,warm,Blue-finned mahseer,0.0224598681704,0.027820540914799997,0.0224598681704,0.027820540914799997 +Cyprinidae,warm,Catla,0.0220743179623,0.0302873745485,0.0220743179623,0.0302873745485 +Cyprinidae,warm,Chocolate mahseer,0.0028306322192,0.0034302133044,0.0028306322192,0.0034302133044 +Cyprinidae,warm,Chub,0.03216167369029999,0.026581131487899996,0.03216167369029999,0.026581131487899996 +Cyprinidae,warm,Common barbel,0.002834382148,0.0023992692516,0.002834382148,0.0023992692516 +Cyprinidae,warm,Common bleak,0.00854949538,0.007072209320000001,0.00854949538,0.007072209320000001 +Cyprinidae,warm,Common bream,0.04600620713676522,0.03642865846998261,0.04600620713676522,0.03642865846998261 +Cyprinidae,warm,Common carp,0.13811330662322036,0.1199294950838376,0.13811330662322036,0.1199294950838376 +Cyprinidae,warm,Common nase,0.026387573782000004,0.023642661213400005,0.026387573782000004,0.023642661213400005 +Cyprinidae,warm,Common roach,0.05312624687117,0.042622199202855,0.05312624687117,0.042622199202855 +Cyprinidae,warm,Crucian carp,0.116377215108,0.10105514804045,0.116377215108,0.10105514804045 +Cyprinidae,warm,Cyprinds,0.032788926016,0.028471980271733335,0.032788926016,0.028471980271733335 +Cyprinidae,warm,European carp,0.13040639766,0.11323726735275001,0.13040639766,0.11323726735275001 +Cyprinidae,warm,European chub,0.001917238372,0.001584568196,0.001917238372,0.001584568196 +Cyprinidae,warm,Ghonia,0.0018287941369,0.0025092223955,0.0018287941369,0.0025092223955 +Cyprinidae,warm,Golden mahseer,0.0034355641156000004,0.0039133067584,0.0034355641156000004,0.0039133067584 +Cyprinidae,warm,Goldfish,0.189583548,0.16462323395,0.189583548,0.16462323395 +Cyprinidae,warm,Grass carp,0.028623161305374997,0.034263768144375,0.028623161305374997,0.034263768144375 +Cyprinidae,warm,Gudgeon,0.0344069672688,0.030376054976399995,0.0344069672688,0.030376054976399995 +Cyprinidae,warm,Italian bleak,0.10233745969859999,0.0846543455604,0.10233745969859999,0.0846543455604 +Cyprinidae,warm,Kalibaus,0.0036833459377,0.0050537859515,0.0036833459377,0.0050537859515 +Cyprinidae,warm,Kutum,2.15702238e-4,1.9326426059999998e-4,2.15702238e-4,1.9326426059999998e-4 +Cyprinidae,warm,Labeo,0.017643999771499996,0.024208694942499998,0.017643999771499996,0.024208694942499998 +Cyprinidae,warm,Mrigal,0.019859158866899998,0.0272480347455,0.019859158866899998,0.0272480347455 +Cyprinidae,warm,Mullet,0.022048241277999998,0.018222534254,0.022048241277999998,0.018222534254 +Cyprinidae,warm,Orange River mudfish,0.01803036473,0.024738812350000004,0.01803036473,0.024738812350000004 +Cyprinidae,warm,Pale chub,0.0023231629616,0.0027809757360000003,0.0023231629616,0.0027809757360000003 +Cyprinidae,warm,Pearl mullet,0.1777012614733,0.14699587071620002,0.1777012614733,0.14699587071620002 +Cyprinidae,warm,Prussian carp,0.064146947634,0.055701447087225,0.064146947634,0.055701447087225 +Cyprinidae,warm,Prussian/Crucian carp,0.0216666912,0.018814083880000002,0.0216666912,0.018814083880000002 +Cyprinidae,warm,Razor fish (ziege),3.7723609910000005e-4,3.294229477e-4,3.7723609910000005e-4,3.294229477e-4 +Cyprinidae,warm,Red garra,0.017643999771499996,0.024208694942499998,0.017643999771499996,0.024208694942499998 +Cyprinidae,warm,Rohu,0.0017515211452,0.0024031989140000003,0.0017515211452,0.0024031989140000003 +Cyprinidae,warm,Rudd,0.010652671243479999,0.00881197281272,0.010652671243479999,0.00881197281272 +Cyprinidae,warm,Rui,0.0368077017131,0.0505025183545,0.0368077017131,0.0505025183545 +Cyprinidae,warm,Shabout,0.0011991616780000001,0.0010150754526,0.0011991616780000001,0.0010150754526 +Cyprinidae,warm,Silver bream,0.010815032339999999,0.008563564440000001,0.010815032339999999,0.008563564440000001 +Cyprinidae,warm,Silver carp,0.029425103982114283,0.035223745194000004,0.029425103982114283,0.035223745194000004 +Cyprinidae,warm,Tench,0.017184651140000003,0.015171401295,0.017184651140000003,0.015171401295 +Cyprinidae,warm,Transcau casian barb,0.014553462183,0.0123193248111,0.014553462183,0.0123193248111 +Cyprinidae,warm,Trout barb,0.004578617316,0.0038757426371999995,0.004578617316,0.0038757426371999995 +Cyprinidae,warm,Vimba bream,0.0212868941635,0.0185888663345,0.0212868941635,0.0185888663345 +Cyprinidae,warm,White Bream,0.043260129359999996,0.034254257760000004,0.043260129359999996,0.034254257760000004 +Percidae,cool,Eurasian Ruffe,0.00486753292,0.0043199098100000005,0.00486753292,0.0043199098100000005 +Percidae,cool,European perch,0.07259463927767063,0.06347059881833862,0.07259463927767063,0.06347059881833862 +Percidae,cool,Golden perch,0.0334276893426,0.0344360226346,0.0334276893426,0.0344360226346 +Percidae,cool,Redfin perch,0.0263136745872,0.0230064464784,0.0263136745872,0.0230064464784 +Percidae,cool,Volga pikeperch,8.01681972e-4,6.487112373e-4,8.01681972e-4,6.487112373e-4 +Percidae,cool,Walleye,0.10268783482319999,0.0983561899176,0.10268783482319999,0.0983561899176 +Percidae,cool,Yellow perch,0.0488076651,0.0456058989,0.0488076651,0.0456058989 +Percidae,cool,Zander (pikeperch),0.037962706268244786,0.032533836325300367,0.037962706268244786,0.032533836325300367 +Salmonidae,cold,Arctic Char,0.0356557719054,0.03500071147903334,0.0356557719054,0.03500071147903334 +Salmonidae,cold,Atlantic salmon,0.265177841604771,0.24011746662753772,0.265177841604771,0.24011746662753772 +Salmonidae,cold,Balkan trout,0.026710147320000002,0.023695444160000002,0.026710147320000002,0.023695444160000002 +Salmonidae,cold,Brook Trout,0.026461932224000003,0.024708687848000004,0.026461932224000003,0.024708687848000004 +Salmonidae,cold,Brown Trout,0.050732109690129576,0.04500611164287132,0.050732109690129576,0.04500611164287132 +Salmonidae,cold,Cherry trout,0.0029371095160000004,0.0027625745076,0.0029371095160000004,0.0027625745076 +Salmonidae,cold,Chinook Salmon (Quinnat Salmon),0.1600752957,0.15222991949999998,0.1600752957,0.15222991949999998 +Salmonidae,cold,Chum salmon,0.02885045315,0.0284171047,0.02885045315,0.0284171047 +Salmonidae,cold,Coho salmon,0.2150947458,0.20066481688,0.2150947458,0.20066481688 +Salmonidae,cold,Common huchen,5.120283490000001e-5,4.81393951e-5,5.120283490000001e-5,4.81393951e-5 +Salmonidae,cold,Dolly varden/Char,0.0034788601644,0.0033581483528,0.0034788601644,0.0033581483528 +Salmonidae,cold,European grayling,0.024065881522700002,0.0212009485031,0.024065881522700002,0.0212009485031 +Salmonidae,cold,Pink salmon,0.025550719900000005,0.02443988385,0.025550719900000005,0.02443988385 +Salmonidae,cold,Rainbow trout,0.06302419030074105,0.05967045472608766,0.06302419030074105,0.05967045472608766 +Salmonidae,cold,Salmon,0.06449020412644443,0.06255334611855555,0.06449020412644443,0.06255334611855555 +Salmonidae,cold,Sockeye salmon,0.029446763100000002,0.029350874649999997,0.029446763100000002,0.029350874649999997 +Salmonidae,cold,Steelhead,0.0049289009400000005,0.00466661704,0.0049289009400000005,0.00466661704 +Salmonidae,cold,Trout,0.0858357697255,0.08267910955158334,0.0858357697255,0.08267910955158334 +Salmonidae,cold,Vendace,0.04116622401,0.0383635935,0.04116622401,0.0383635935 +Salmonidae,cold,Whitefish,0.05007684609903145,0.04666757308221,0.05007684609903145,0.04666757308221 +Siluridae,warm,Boal,0.0124330956288,0.0105118070912,0.0124330956288,0.0105118070912 +Siluridae,warm,Catfish,0.033215868223999995,0.028083014042666665,0.033215868223999995,0.028083014042666665 +Siluridae,warm,European catfish,0.016684117296,0.014105917603999998,0.016684117296,0.014105917603999998 +Siluridae,warm,Far eastern catfish,0.0031082739072,0.0026279517728,0.0031082739072,0.0026279517728 +Siluridae,warm,Wels catfish,0.0333433019136,0.028190755380945457,0.0333433019136,0.028190755380945457 diff --git a/src/components/FishAsFoodLinkChartViz.vue b/src/components/FishAsFoodLinkChartViz.vue index aa884e6..368c972 100644 --- a/src/components/FishAsFoodLinkChartViz.vue +++ b/src/components/FishAsFoodLinkChartViz.vue @@ -37,6 +37,7 @@ const dataFile = 'fish_as_food_climate.csv' //'fish_as_food_climate_test.csv' const data = ref(); const chart = ref(null); + const scalePercent = ref(); // const families = ref(); let chartDimensions; const chartTitle = 'Title of chart'; @@ -65,13 +66,17 @@ // }); // expandedFamilies["A"] = !expandedFamilies["A"] // console.log(expandedFamilies) + // Set starting scale type + scalePercent.value = true; + initChart({ width: chart.value.offsetWidth, height: 1800, margin: 20, marginBottom: 60, marginLeft: 200}); - drawChart(data.value); + + drawChart(data.value, scalePercent.value); } else { console.error('Error loading data'); } @@ -274,16 +279,16 @@ return itemColor; } - function drawChart(data) { + function drawChart(data, scalePercent) { // accessor functions const yAccessor = d => d.species // const yAccessor_family = d => d.family const xAccessor = d => d.cvi // const xAccessor_family = d => d.cvi_family - const x0Accessor = d => d.cvi_2030 - const x1Accessor = d => d.cvi_2075 - const x0Accessor_family = d => d.cvi_2030_family - const x1Accessor_family = d => d.cvi_2075_family + const x0Accessor = d => scalePercent ? 0 : d.cvi_2030 + const x1Accessor = d => scalePercent ? (d.cvi_2075 - d.cvi_2030)/ d.cvi_2030 : d.cvi_2075 + const x0Accessor_family = d => scalePercent ? 0 : d.cvi_2030_family + const x1Accessor_family = d => scalePercent ? (d.cvi_2075_family - d.cvi_2030_family)/ d.cvi_2030_family : d.cvi_2075_family const widthAccessor = d => d.position const colorAccessor = d => d.thermal_guild const identifierAccessor = d => d.family + '_' + d.species @@ -430,7 +435,7 @@ // yScale.range([chartDimensions.boundedHeight * 2, 0]) // yAxis // .call(d3.axisLeft(yScale).tickSize(2)) - // drawChart(data) + // drawChart(data, scalePercent.value) // }); chartBounds.append("g") -- GitLab From d3e3ef88418c02b90d2d5008ce3c4b100a40b56c Mon Sep 17 00:00:00 2001 From: Corson-Dosch <hcorson-dosch@usgs.gov> Date: Tue, 27 Aug 2024 14:07:54 -0500 Subject: [PATCH 06/25] start implementing enter update exit pattern --- src/components/FishAsFoodLinkChartViz.vue | 126 ++++++++++++++++++---- 1 file changed, 103 insertions(+), 23 deletions(-) diff --git a/src/components/FishAsFoodLinkChartViz.vue b/src/components/FishAsFoodLinkChartViz.vue index 368c972..66a47fb 100644 --- a/src/components/FishAsFoodLinkChartViz.vue +++ b/src/components/FishAsFoodLinkChartViz.vue @@ -12,6 +12,14 @@ </template> <template #figures> <div id="chart-container" class="maxWidth" ref="chart"></div> + <button + aria-pressed="!scalePercent" + class="button" + :text="scaleType" + @click="toggleScale" + > + {{ scaleType }} + </button> </template> <!-- FIGURE CAPTION --> <template #figureCaption> @@ -23,7 +31,7 @@ </template> <script setup> - import { onMounted, ref } from "vue"; //, reactive + import { computed, onMounted, ref } from "vue"; //, reactive import * as d3 from 'd3'; import VizSection from '@/components/VizSection.vue'; @@ -37,8 +45,7 @@ const dataFile = 'fish_as_food_climate.csv' //'fish_as_food_climate_test.csv' const data = ref(); const chart = ref(null); - const scalePercent = ref(); - // const families = ref(); + const scalePercent = ref(false); let chartDimensions; const chartTitle = 'Title of chart'; let chartSVG; @@ -52,6 +59,11 @@ // Create a reactive object to track expanded families // const expandedFamilies = reactive({}); + // set up filtered chart data as computed property + const scaleType = computed(() => { + return scalePercent.value ? 'Percent change' : 'Change' + }); + // Behavior on mounted (functions called here) // Load data and then make chart @@ -67,7 +79,7 @@ // expandedFamilies["A"] = !expandedFamilies["A"] // console.log(expandedFamilies) // Set starting scale type - scalePercent.value = true; + // scalePercent.value = true; initChart({ width: chart.value.offsetWidth, @@ -96,7 +108,13 @@ async function loadData(fileName) { try { - const data = await d3.csv(publicPath + fileName); + const data = await d3.csv(publicPath + fileName, d => { + d.cvi_2030 = +d.cvi_2030; + d.cvi_2075 = +d.cvi_2075; + d.cvi_2030_family = +d.cvi_2030_family; + d.cvi_2075_family = +d.cvi_2075_family; + return d; + }); return data; } catch (error) { console.error(`Error loading data from ${fileName}`, error); @@ -104,6 +122,11 @@ } } + function toggleScale() { + scalePercent.value = !scalePercent.value + drawChart(data.value, scalePercent.value) + } + function initChart({ width = 500, // outer width, in pixels height = width, // outer height, in pixels @@ -291,7 +314,7 @@ const x1Accessor_family = d => scalePercent ? (d.cvi_2075_family - d.cvi_2030_family)/ d.cvi_2030_family : d.cvi_2075_family const widthAccessor = d => d.position const colorAccessor = d => d.thermal_guild - const identifierAccessor = d => d.family + '_' + d.species + const identifierAccessor = d => d.family + '_' + d.species.replace(/ /g,"_"); // to get dynamic // need key for data @@ -300,13 +323,21 @@ // set domain for xScale - xScale - // .domain([0, 0.3]) // d3.max([d3.max(data, x0Accessor), d3.max(data, x1Accessor)])]) - .domain([-0.4, 0.4]) - console.log(xScale.domain()) + if (scalePercent) { + const maxVal = d3.max([Math.abs(d3.max(data, x1Accessor)), Math.abs(d3.min(data, x1Accessor))]) + xScale + .domain([-maxVal, maxVal]) + .nice() + } else { + const maxVal = d3.max([d3.min(data, x0Accessor), d3.max(data, x0Accessor), d3.min(data, x1Accessor), d3.max(data, x1Accessor)]) + xScale + .domain([0, Math.round(maxVal * 10) / 10]) + .nice() + } - xAxis - .call(d3.axisBottom(xScale).tickSize(0).tickPadding(10)) + xAxis.transition() + .duration(500) + .call(d3.axisBottom(xScale).tickSize(0).tickPadding(10)); xAxis .selectAll("text") @@ -320,7 +351,7 @@ .attr("y", xAxisLabelYPosition * 4) .attr("dy", xAxisLabelDy) .style("text-anchor", "middle") - .text('Percent change in harvest-weighted climate vulnerability, 2030-2075') + .text(scalePercent ? 'Percent change in harvest-weighted climate vulnerability, 2030-2075' : 'Change in harvest-weighted climate vulnerability, 2030-2075') // xAxis.append("text") // .attr("class", "x-axis axis-title") // .attr("x", 0) @@ -411,17 +442,54 @@ }) // draw chart - chartBounds.append("g") + let areaGroups = chartBounds.append("g") .attr("id", "areas") - .selectAll('.area') - .data(areaData) - .enter() - .append('path') - .attr("id", d => 'area-2030-' + identifierAccessor(d)) - .attr('class', "area") - .attr('d', d => area(d)) - .attr('fill', d => d[0].cvi_decreasing ? `url(#${d[0].thermal_guild}_gradient_decreasing)`: `url(#${d[0].thermal_guild}_gradient_increasing)`)//d => colorScale(colorAccessor(d[0]))) - .style("opacity", 1) + .selectAll(".area") + .data(areaData, d => d[0].species) + + const oldAreaGroups = areaGroups.exit() + + oldAreaGroups.selectAll('path') + .transition(getExitTransition()) + .style("opacity", 0) + + oldAreaGroups.transition(getExitTransition()).remove() + + const newAreaGroups = areaGroups.enter().append("g") + .attr("class", d => "area " + d[0].species) + .attr("id", d => 'area-group-' + identifierAccessor(d[0])) + + // append paths + newAreaGroups.append("path") + .append('path') + .attr("id", d => 'area-2030-' + identifierAccessor(d[0])) + .attr('d', d => area(d)) + .attr('fill', d => d[0].cvi_decreasing ? `url(#${d[0].thermal_guild}_gradient_decreasing)`: `url(#${d[0].thermal_guild}_gradient_increasing)`)//d => colorScale(colorAccessor(d[0]))) + .style("opacity", 1) + + // update rectGroups to include new points + areaGroups = newAreaGroups.merge(areaGroups) + + const areaPaths = areaGroups.select("path") + + // Update bars based on data values + areaPaths.transition(getUpdateTransition()) + .attr("id", d => 'area-2030-' + identifierAccessor(d[0])) + .attr('d', d => area(d)) + .attr('fill', d => d[0].cvi_decreasing ? `url(#${d[0].thermal_guild}_gradient_decreasing)`: `url(#${d[0].thermal_guild}_gradient_increasing)`)//d => colorScale(colorAccessor(d[0]))) + .style("opacity", 1) + + // chartBounds.append("g") + // .attr("id", "areas") + // .selectAll('.area') + // .data(areaData, d => d[0].species) + // .enter() + // .append('path') + // .attr("id", d => 'area-2030-' + identifierAccessor(d[0])) + // .attr('class', "area") + // .attr('d', d => area(d)) + // .attr('fill', d => d[0].cvi_decreasing ? `url(#${d[0].thermal_guild}_gradient_decreasing)`: `url(#${d[0].thermal_guild}_gradient_increasing)`)//d => colorScale(colorAccessor(d[0]))) + // .style("opacity", 1) // .on("click", function(event, d) { // const clickedFamily = d[0].family // expandedFamilies[clickedFamily] = !expandedFamilies[clickedFamily] @@ -438,6 +506,7 @@ // drawChart(data, scalePercent.value) // }); + chartBounds.append("g") .attr("id", "points-2030") .attr("class", "points points_2030") @@ -487,6 +556,17 @@ // .style("fill", "black") } + + function getUpdateTransition () { + return d3.transition() + .duration(500) + .ease(d3.easeCubicInOut) + } + function getExitTransition() { + return d3.transition() + .duration(500) + .ease(d3.easeCubicInOut) + } </script> <style lang="scss"> -- GitLab From c7fb294adeb08903da1ef8e1eae6bab5a59c915d Mon Sep 17 00:00:00 2001 From: Corson-Dosch <hcorson-dosch@usgs.gov> Date: Tue, 27 Aug 2024 17:18:28 -0500 Subject: [PATCH 07/25] get enter update exit working for areas and points --- public/fish_as_food_climate.csv | 128 ++++++++-------- src/components/FishAsFoodLinkChartViz.vue | 178 +++++++++++++++++----- 2 files changed, 200 insertions(+), 106 deletions(-) diff --git a/public/fish_as_food_climate.csv b/public/fish_as_food_climate.csv index 74ed7c5..a64a562 100644 --- a/public/fish_as_food_climate.csv +++ b/public/fish_as_food_climate.csv @@ -1,84 +1,84 @@ family,thermal_guild,species,cvi_2030,cvi_2075,cvi_2030_family,cvi_2075_family -Cyprinidae,warm,Albanian roach,0.10778174260000001,0.0941208422,0.10778174260000001,0.0941208422 +Cyprinidae,warm,Albanian roach,0.10778174260000001,0.094120842,0.10778174260000001,0.094120842 Cyprinidae,warm,Asp,0.013429605127960001,0.011727456938119999,0.013429605127960001,0.011727456938119999 -Cyprinidae,warm,Barbel,0.04705801130333334,0.039834021549,0.04705801130333334,0.039834021549 -Cyprinidae,warm,Bata,0.0055121400746,0.007563008347000001,0.0055121400746,0.007563008347000001 +Cyprinidae,warm,Barbel,0.047058011,0.039834022,0.047058011,0.039834022 +Cyprinidae,warm,Bata,0.00551214,0.007563008,0.00551214,0.007563008 Cyprinidae,warm,BeyÅŸehir bleak,7.267071073e-4,6.011377922000001e-4,7.267071073e-4,6.011377922000001e-4 Cyprinidae,warm,Black bream,0.051246241799999995,0.061345053,0.051246241799999995,0.061345053 Cyprinidae,warm,Black carp,0.051246241799999995,0.061345053,0.051246241799999995,0.061345053 -Cyprinidae,warm,Blue-finned mahseer,0.0224598681704,0.027820540914799997,0.0224598681704,0.027820540914799997 -Cyprinidae,warm,Catla,0.0220743179623,0.0302873745485,0.0220743179623,0.0302873745485 -Cyprinidae,warm,Chocolate mahseer,0.0028306322192,0.0034302133044,0.0028306322192,0.0034302133044 -Cyprinidae,warm,Chub,0.03216167369029999,0.026581131487899996,0.03216167369029999,0.026581131487899996 -Cyprinidae,warm,Common barbel,0.002834382148,0.0023992692516,0.002834382148,0.0023992692516 -Cyprinidae,warm,Common bleak,0.00854949538,0.007072209320000001,0.00854949538,0.007072209320000001 -Cyprinidae,warm,Common bream,0.04600620713676522,0.03642865846998261,0.04600620713676522,0.03642865846998261 +Cyprinidae,warm,Blue-finned mahseer,0.022459868,0.027820540914799997,0.022459868,0.027820540914799997 +Cyprinidae,warm,Catla,0.022074318,0.030287375,0.022074318,0.030287375 +Cyprinidae,warm,Chocolate mahseer,0.002830632,0.003430213,0.002830632,0.003430213 +Cyprinidae,warm,Chub,0.032161674,0.026581131487899996,0.032161674,0.026581131487899996 +Cyprinidae,warm,Common barbel,0.002834382,0.002399269,0.002834382,0.002399269 +Cyprinidae,warm,Common bleak,0.008549495,0.007072209,0.008549495,0.007072209 +Cyprinidae,warm,Common bream,0.046006207,0.036428658,0.046006207,0.036428658 Cyprinidae,warm,Common carp,0.13811330662322036,0.1199294950838376,0.13811330662322036,0.1199294950838376 Cyprinidae,warm,Common nase,0.026387573782000004,0.023642661213400005,0.026387573782000004,0.023642661213400005 -Cyprinidae,warm,Common roach,0.05312624687117,0.042622199202855,0.05312624687117,0.042622199202855 -Cyprinidae,warm,Crucian carp,0.116377215108,0.10105514804045,0.116377215108,0.10105514804045 -Cyprinidae,warm,Cyprinds,0.032788926016,0.028471980271733335,0.032788926016,0.028471980271733335 -Cyprinidae,warm,European carp,0.13040639766,0.11323726735275001,0.13040639766,0.11323726735275001 -Cyprinidae,warm,European chub,0.001917238372,0.001584568196,0.001917238372,0.001584568196 -Cyprinidae,warm,Ghonia,0.0018287941369,0.0025092223955,0.0018287941369,0.0025092223955 -Cyprinidae,warm,Golden mahseer,0.0034355641156000004,0.0039133067584,0.0034355641156000004,0.0039133067584 -Cyprinidae,warm,Goldfish,0.189583548,0.16462323395,0.189583548,0.16462323395 -Cyprinidae,warm,Grass carp,0.028623161305374997,0.034263768144375,0.028623161305374997,0.034263768144375 -Cyprinidae,warm,Gudgeon,0.0344069672688,0.030376054976399995,0.0344069672688,0.030376054976399995 -Cyprinidae,warm,Italian bleak,0.10233745969859999,0.0846543455604,0.10233745969859999,0.0846543455604 -Cyprinidae,warm,Kalibaus,0.0036833459377,0.0050537859515,0.0036833459377,0.0050537859515 +Cyprinidae,warm,Common roach,0.053126247,0.042622199,0.053126247,0.042622199 +Cyprinidae,warm,Crucian carp,0.116377215,0.101055148,0.116377215,0.101055148 +Cyprinidae,warm,Cyprinds,0.032788926,0.028471980271733335,0.032788926,0.028471980271733335 +Cyprinidae,warm,European carp,0.130406398,0.11323726735275001,0.130406398,0.11323726735275001 +Cyprinidae,warm,European chub,0.001917238,0.001584568,0.001917238,0.001584568 +Cyprinidae,warm,Ghonia,0.001828794,0.002509222,0.001828794,0.002509222 +Cyprinidae,warm,Golden mahseer,0.003435564,0.003913307,0.003435564,0.003913307 +Cyprinidae,warm,Goldfish,0.189583548,0.164623234,0.189583548,0.164623234 +Cyprinidae,warm,Grass carp,0.028623161305374997,0.034263768,0.028623161305374997,0.034263768 +Cyprinidae,warm,Gudgeon,0.034406967,0.030376054976399995,0.034406967,0.030376054976399995 +Cyprinidae,warm,Italian bleak,0.10233745969859999,0.084654346,0.10233745969859999,0.084654346 +Cyprinidae,warm,Kalibaus,0.003683346,0.005053786,0.003683346,0.005053786 Cyprinidae,warm,Kutum,2.15702238e-4,1.9326426059999998e-4,2.15702238e-4,1.9326426059999998e-4 Cyprinidae,warm,Labeo,0.017643999771499996,0.024208694942499998,0.017643999771499996,0.024208694942499998 -Cyprinidae,warm,Mrigal,0.019859158866899998,0.0272480347455,0.019859158866899998,0.0272480347455 -Cyprinidae,warm,Mullet,0.022048241277999998,0.018222534254,0.022048241277999998,0.018222534254 -Cyprinidae,warm,Orange River mudfish,0.01803036473,0.024738812350000004,0.01803036473,0.024738812350000004 -Cyprinidae,warm,Pale chub,0.0023231629616,0.0027809757360000003,0.0023231629616,0.0027809757360000003 -Cyprinidae,warm,Pearl mullet,0.1777012614733,0.14699587071620002,0.1777012614733,0.14699587071620002 -Cyprinidae,warm,Prussian carp,0.064146947634,0.055701447087225,0.064146947634,0.055701447087225 -Cyprinidae,warm,Prussian/Crucian carp,0.0216666912,0.018814083880000002,0.0216666912,0.018814083880000002 +Cyprinidae,warm,Mrigal,0.019859158866899998,0.027248035,0.019859158866899998,0.027248035 +Cyprinidae,warm,Mullet,0.022048241277999998,0.018222534,0.022048241277999998,0.018222534 +Cyprinidae,warm,Orange River mudfish,0.018030365,0.024738812350000004,0.018030365,0.024738812350000004 +Cyprinidae,warm,Pale chub,0.002323163,0.002780976,0.002323163,0.002780976 +Cyprinidae,warm,Pearl mullet,0.177701261,0.14699587071620002,0.177701261,0.14699587071620002 +Cyprinidae,warm,Prussian carp,0.064146948,0.055701447,0.064146948,0.055701447 +Cyprinidae,warm,Prussian/Crucian carp,0.021666691,0.018814083880000002,0.021666691,0.018814083880000002 Cyprinidae,warm,Razor fish (ziege),3.7723609910000005e-4,3.294229477e-4,3.7723609910000005e-4,3.294229477e-4 Cyprinidae,warm,Red garra,0.017643999771499996,0.024208694942499998,0.017643999771499996,0.024208694942499998 -Cyprinidae,warm,Rohu,0.0017515211452,0.0024031989140000003,0.0017515211452,0.0024031989140000003 -Cyprinidae,warm,Rudd,0.010652671243479999,0.00881197281272,0.010652671243479999,0.00881197281272 -Cyprinidae,warm,Rui,0.0368077017131,0.0505025183545,0.0368077017131,0.0505025183545 -Cyprinidae,warm,Shabout,0.0011991616780000001,0.0010150754526,0.0011991616780000001,0.0010150754526 -Cyprinidae,warm,Silver bream,0.010815032339999999,0.008563564440000001,0.010815032339999999,0.008563564440000001 +Cyprinidae,warm,Rohu,0.001751521,0.002403199,0.001751521,0.002403199 +Cyprinidae,warm,Rudd,0.010652671243479999,0.008811973,0.010652671243479999,0.008811973 +Cyprinidae,warm,Rui,0.036807702,0.050502518,0.036807702,0.050502518 +Cyprinidae,warm,Shabout,0.001199162,0.001015075,0.001199162,0.001015075 +Cyprinidae,warm,Silver bream,0.010815032339999999,0.008563564,0.010815032339999999,0.008563564 Cyprinidae,warm,Silver carp,0.029425103982114283,0.035223745194000004,0.029425103982114283,0.035223745194000004 -Cyprinidae,warm,Tench,0.017184651140000003,0.015171401295,0.017184651140000003,0.015171401295 -Cyprinidae,warm,Transcau casian barb,0.014553462183,0.0123193248111,0.014553462183,0.0123193248111 -Cyprinidae,warm,Trout barb,0.004578617316,0.0038757426371999995,0.004578617316,0.0038757426371999995 -Cyprinidae,warm,Vimba bream,0.0212868941635,0.0185888663345,0.0212868941635,0.0185888663345 +Cyprinidae,warm,Tench,0.017184651140000003,0.015171401,0.017184651140000003,0.015171401 +Cyprinidae,warm,Transcau casian barb,0.014553462,0.012319325,0.014553462,0.012319325 +Cyprinidae,warm,Trout barb,0.004578617,0.003875743,0.004578617,0.003875743 +Cyprinidae,warm,Vimba bream,0.021286894,0.018588866,0.021286894,0.018588866 Cyprinidae,warm,White Bream,0.043260129359999996,0.034254257760000004,0.043260129359999996,0.034254257760000004 -Percidae,cool,Eurasian Ruffe,0.00486753292,0.0043199098100000005,0.00486753292,0.0043199098100000005 -Percidae,cool,European perch,0.07259463927767063,0.06347059881833862,0.07259463927767063,0.06347059881833862 -Percidae,cool,Golden perch,0.0334276893426,0.0344360226346,0.0334276893426,0.0344360226346 -Percidae,cool,Redfin perch,0.0263136745872,0.0230064464784,0.0263136745872,0.0230064464784 +Percidae,cool,Eurasian Ruffe,0.004867533,0.00431991,0.004867533,0.00431991 +Percidae,cool,European perch,0.072594639,0.063470599,0.072594639,0.063470599 +Percidae,cool,Golden perch,0.033427689,0.034436023,0.033427689,0.034436023 +Percidae,cool,Redfin perch,0.026313675,0.023006446,0.026313675,0.023006446 Percidae,cool,Volga pikeperch,8.01681972e-4,6.487112373e-4,8.01681972e-4,6.487112373e-4 -Percidae,cool,Walleye,0.10268783482319999,0.0983561899176,0.10268783482319999,0.0983561899176 -Percidae,cool,Yellow perch,0.0488076651,0.0456058989,0.0488076651,0.0456058989 +Percidae,cool,Walleye,0.10268783482319999,0.09835619,0.10268783482319999,0.09835619 +Percidae,cool,Yellow perch,0.048807665,0.045605899,0.048807665,0.045605899 Percidae,cool,Zander (pikeperch),0.037962706268244786,0.032533836325300367,0.037962706268244786,0.032533836325300367 -Salmonidae,cold,Arctic Char,0.0356557719054,0.03500071147903334,0.0356557719054,0.03500071147903334 -Salmonidae,cold,Atlantic salmon,0.265177841604771,0.24011746662753772,0.265177841604771,0.24011746662753772 +Salmonidae,cold,Arctic Char,0.035655772,0.035000711,0.035655772,0.035000711 +Salmonidae,cold,Atlantic salmon,0.265177842,0.24011746662753772,0.265177842,0.24011746662753772 Salmonidae,cold,Balkan trout,0.026710147320000002,0.023695444160000002,0.026710147320000002,0.023695444160000002 Salmonidae,cold,Brook Trout,0.026461932224000003,0.024708687848000004,0.026461932224000003,0.024708687848000004 -Salmonidae,cold,Brown Trout,0.050732109690129576,0.04500611164287132,0.050732109690129576,0.04500611164287132 -Salmonidae,cold,Cherry trout,0.0029371095160000004,0.0027625745076,0.0029371095160000004,0.0027625745076 -Salmonidae,cold,Chinook Salmon (Quinnat Salmon),0.1600752957,0.15222991949999998,0.1600752957,0.15222991949999998 -Salmonidae,cold,Chum salmon,0.02885045315,0.0284171047,0.02885045315,0.0284171047 -Salmonidae,cold,Coho salmon,0.2150947458,0.20066481688,0.2150947458,0.20066481688 +Salmonidae,cold,Brown Trout,0.050732109690129576,0.045006112,0.050732109690129576,0.045006112 +Salmonidae,cold,Cherry trout,0.00293711,0.002762575,0.00293711,0.002762575 +Salmonidae,cold,Chinook Salmon (Quinnat Salmon),0.160075296,0.15222991949999998,0.160075296,0.15222991949999998 +Salmonidae,cold,Chum salmon,0.028850453,0.028417105,0.028850453,0.028417105 +Salmonidae,cold,Coho salmon,0.215094746,0.200664817,0.215094746,0.200664817 Salmonidae,cold,Common huchen,5.120283490000001e-5,4.81393951e-5,5.120283490000001e-5,4.81393951e-5 -Salmonidae,cold,Dolly varden/Char,0.0034788601644,0.0033581483528,0.0034788601644,0.0033581483528 -Salmonidae,cold,European grayling,0.024065881522700002,0.0212009485031,0.024065881522700002,0.0212009485031 -Salmonidae,cold,Pink salmon,0.025550719900000005,0.02443988385,0.025550719900000005,0.02443988385 -Salmonidae,cold,Rainbow trout,0.06302419030074105,0.05967045472608766,0.06302419030074105,0.05967045472608766 -Salmonidae,cold,Salmon,0.06449020412644443,0.06255334611855555,0.06449020412644443,0.06255334611855555 +Salmonidae,cold,Dolly varden/Char,0.00347886,0.003358148,0.00347886,0.003358148 +Salmonidae,cold,European grayling,0.024065881522700002,0.021200949,0.024065881522700002,0.021200949 +Salmonidae,cold,Pink salmon,0.025550719900000005,0.024439884,0.025550719900000005,0.024439884 +Salmonidae,cold,Rainbow trout,0.06302419,0.059670455,0.06302419,0.059670455 +Salmonidae,cold,Salmon,0.064490204,0.062553346,0.064490204,0.062553346 Salmonidae,cold,Sockeye salmon,0.029446763100000002,0.029350874649999997,0.029446763100000002,0.029350874649999997 -Salmonidae,cold,Steelhead,0.0049289009400000005,0.00466661704,0.0049289009400000005,0.00466661704 -Salmonidae,cold,Trout,0.0858357697255,0.08267910955158334,0.0858357697255,0.08267910955158334 -Salmonidae,cold,Vendace,0.04116622401,0.0383635935,0.04116622401,0.0383635935 -Salmonidae,cold,Whitefish,0.05007684609903145,0.04666757308221,0.05007684609903145,0.04666757308221 -Siluridae,warm,Boal,0.0124330956288,0.0105118070912,0.0124330956288,0.0105118070912 +Salmonidae,cold,Steelhead,0.004928901,0.004666617,0.004928901,0.004666617 +Salmonidae,cold,Trout,0.08583577,0.08267911,0.08583577,0.08267911 +Salmonidae,cold,Vendace,0.041166224,0.038363594,0.041166224,0.038363594 +Salmonidae,cold,Whitefish,0.050076846,0.046667573,0.050076846,0.046667573 +Siluridae,warm,Boal,0.012433096,0.010511807,0.012433096,0.010511807 Siluridae,warm,Catfish,0.033215868223999995,0.028083014042666665,0.033215868223999995,0.028083014042666665 -Siluridae,warm,European catfish,0.016684117296,0.014105917603999998,0.016684117296,0.014105917603999998 -Siluridae,warm,Far eastern catfish,0.0031082739072,0.0026279517728,0.0031082739072,0.0026279517728 -Siluridae,warm,Wels catfish,0.0333433019136,0.028190755380945457,0.0333433019136,0.028190755380945457 +Siluridae,warm,European catfish,0.016684117,0.014105917603999998,0.016684117,0.014105917603999998 +Siluridae,warm,Far eastern catfish,0.003108274,0.002627952,0.003108274,0.002627952 +Siluridae,warm,Wels catfish,0.033343302,0.028190755380945457,0.033343302,0.028190755380945457 diff --git a/src/components/FishAsFoodLinkChartViz.vue b/src/components/FishAsFoodLinkChartViz.vue index 66a47fb..550bb66 100644 --- a/src/components/FishAsFoodLinkChartViz.vue +++ b/src/components/FishAsFoodLinkChartViz.vue @@ -178,6 +178,14 @@ // Inititalize gradients initGradients() + + // Add groups for visual elements + chartBounds.append("g") + .attr("class", "areas") + chartBounds.append("g") + .attr("class", "points_2030") + chartBounds.append("g") + .attr("class", "points_2075") } function initXScale() { @@ -335,8 +343,7 @@ .nice() } - xAxis.transition() - .duration(500) + xAxis.transition(getUpdateTransition()) .call(d3.axisBottom(xScale).tickSize(0).tickPadding(10)); xAxis @@ -442,11 +449,11 @@ }) // draw chart - let areaGroups = chartBounds.append("g") - .attr("id", "areas") + // Enter-update-exit pattern for areas + let areaGroups = chartBounds.selectAll(".areas") .selectAll(".area") .data(areaData, d => d[0].species) - + const oldAreaGroups = areaGroups.exit() oldAreaGroups.selectAll('path') @@ -462,19 +469,19 @@ // append paths newAreaGroups.append("path") .append('path') - .attr("id", d => 'area-2030-' + identifierAccessor(d[0])) - .attr('d', d => area(d)) + .attr("id", d => 'area-' + identifierAccessor(d[0])) + .attr('d', null) .attr('fill', d => d[0].cvi_decreasing ? `url(#${d[0].thermal_guild}_gradient_decreasing)`: `url(#${d[0].thermal_guild}_gradient_increasing)`)//d => colorScale(colorAccessor(d[0]))) .style("opacity", 1) - // update rectGroups to include new points + // update areaGroups to include new paths areaGroups = newAreaGroups.merge(areaGroups) const areaPaths = areaGroups.select("path") - // Update bars based on data values + // Update paths based on data values areaPaths.transition(getUpdateTransition()) - .attr("id", d => 'area-2030-' + identifierAccessor(d[0])) + .attr("id", d => 'area-' + identifierAccessor(d[0])) .attr('d', d => area(d)) .attr('fill', d => d[0].cvi_decreasing ? `url(#${d[0].thermal_guild}_gradient_decreasing)`: `url(#${d[0].thermal_guild}_gradient_increasing)`)//d => colorScale(colorAccessor(d[0]))) .style("opacity", 1) @@ -485,7 +492,7 @@ // .data(areaData, d => d[0].species) // .enter() // .append('path') - // .attr("id", d => 'area-2030-' + identifierAccessor(d[0])) + // .attr("id", d => 'area-' + identifierAccessor(d[0])) // .attr('class', "area") // .attr('d', d => area(d)) // .attr('fill', d => d[0].cvi_decreasing ? `url(#${d[0].thermal_guild}_gradient_decreasing)`: `url(#${d[0].thermal_guild}_gradient_increasing)`)//d => colorScale(colorAccessor(d[0]))) @@ -506,37 +513,124 @@ // drawChart(data, scalePercent.value) // }); + // Enter-Update-Exit pattern for 2030 points + let pointGroups2030 = chartBounds.selectAll('.points_2030') + .selectAll(".point_2030") + .data(data, d => d.species) + + const oldPointGroups2030 = pointGroups2030.exit() + + oldPointGroups2030.selectAll('circle') + .transition(getExitTransition()) + .style("opacity", 0) - chartBounds.append("g") - .attr("id", "points-2030") - .attr("class", "points points_2030") - .selectAll('points') - .data(data) - .enter() - .append("circle") - .attr("id", d => 'point-2030-' + identifierAccessor(d)) - .attr("class", "point") - .attr("cx", d => xScale(x0Accessor(d))) - .attr("cy", d => yScale(yAccessor(d)) + yScale.bandwidth() / 2) - .attr("r", radiusPosition0) - .style("stroke", d => colorScale(colorAccessor(d))) - .style("fill", "white") + oldPointGroups2030.transition(getExitTransition()).remove() + + const newPointGroups2030 = pointGroups2030.enter().append("g") + .attr("class", d => "point_2030 " + d.species) + .attr("id", d => 'point-2030-group-' + identifierAccessor(d)) + + // append points + newPointGroups2030 + .append("circle") + .attr("id", d => 'point-2030-' + identifierAccessor(d)) + .attr("class", "point_2030") + .attr("cx", d => xScale(x0Accessor(d))) + .attr("cy", d => yScale(yAccessor(d)) + yScale.bandwidth() / 2) + .attr("r", radiusPosition0) + .style("stroke", d => colorScale(colorAccessor(d))) + .style("fill", "white") + + // update pointGroups2030 to include new points + pointGroups2030 = newPointGroups2030.merge(pointGroups2030) + + const allPoints2030 = pointGroups2030.select("circle") + + // Update points based on data values + allPoints2030.transition(getUpdateTransition()) + .attr("id", d => 'point-2030-' + identifierAccessor(d)) + .attr("class", "point_2030") + .attr("cx", d => xScale(x0Accessor(d))) + .attr("cy", d => yScale(yAccessor(d)) + yScale.bandwidth() / 2) + .attr("r", radiusPosition0) + .style("stroke", d => colorScale(colorAccessor(d))) + .style("fill", "white") - chartBounds.append("g") - .attr("id", "points-2075") - .attr("class", "points points_2075") - .selectAll('points') - .data(data) - .enter() - .append("circle") - .attr("id", d => 'point-2075-' + identifierAccessor(d)) - .attr("class", "point") - .attr("cx", d => xScale(x1Accessor(d))) - .attr("cy", d => yScale(yAccessor(d)) + yScale.bandwidth() / 2) - .attr("r", radiusPosition1 - strokeWidth1 / 2) - .style("stroke", d => colorScale(colorAccessor(d))) - .style("stroke-width", strokeWidth1) - .style("fill", "white") + // chartBounds.append("g") + // .attr("id", "points-2030") + // .attr("class", "points points_2030") + // .selectAll('points') + // .data(data) + // .enter() + // .append("circle") + // .attr("id", d => 'point-2030-' + identifierAccessor(d)) + // .attr("class", "point") + // .attr("cx", d => xScale(x0Accessor(d))) + // .attr("cy", d => yScale(yAccessor(d)) + yScale.bandwidth() / 2) + // .attr("r", radiusPosition0) + // .style("stroke", d => colorScale(colorAccessor(d))) + // .style("fill", "white") + + // Enter-Update-Exit pattern for 2075 points + let pointGroups2075 = chartBounds.selectAll('.points_2075') + .selectAll(".point_2075") + .data(data, d => d.species) + + const oldPointGroups2075 = pointGroups2075.exit() + + oldPointGroups2075.selectAll('circle') + .transition(getExitTransition()) + .style("opacity", 0) + + oldPointGroups2075.transition(getExitTransition()).remove() + + const newPointGroups2075 = pointGroups2075.enter().append("g") + .attr("class", d => "point_2075 " + d.species) + .attr("id", d => 'point-2030-group-' + identifierAccessor(d)) + + // append points + newPointGroups2075 + .append("circle") + .attr("id", d => 'point-2075-' + identifierAccessor(d)) + .attr("class", "point_2075") + .attr("cx", d => xScale(x1Accessor(d))) + .attr("cy", d => yScale(yAccessor(d)) + yScale.bandwidth() / 2) + .attr("r", radiusPosition1 - strokeWidth1 / 2) + .style("stroke", d => colorScale(colorAccessor(d))) + .style("stroke-width", strokeWidth1) + .style("fill", "white") + + // update pointGroups2075 to include new points + pointGroups2075 = newPointGroups2075.merge(pointGroups2075) + + const allPoints2075 = pointGroups2075.select("circle") + + // Update points based on data values + allPoints2075.transition(getUpdateTransition()) + .attr("id", d => 'point-2075-' + identifierAccessor(d)) + .attr("class", "point_2075") + .attr("cx", d => xScale(x1Accessor(d))) + .attr("cy", d => yScale(yAccessor(d)) + yScale.bandwidth() / 2) + .attr("r", radiusPosition1 - strokeWidth1 / 2) + .style("stroke", d => colorScale(colorAccessor(d))) + .style("stroke-width", strokeWidth1) + .style("fill", "white") + + // chartBounds.append("g") + // .attr("id", "points-2075") + // .attr("class", "points points_2075") + // .selectAll('points') + // .data(data) + // .enter() + // .append("circle") + // .attr("id", d => 'point-2075-' + identifierAccessor(d)) + // .attr("class", "point") + // .attr("cx", d => xScale(x1Accessor(d))) + // .attr("cy", d => yScale(yAccessor(d)) + yScale.bandwidth() / 2) + // .attr("r", radiusPosition1 - strokeWidth1 / 2) + // .style("stroke", d => colorScale(colorAccessor(d))) + // .style("stroke-width", strokeWidth1) + // .style("fill", "white") // chartBounds.append("g") // .attr("id", "eyes-2075") @@ -559,12 +653,12 @@ function getUpdateTransition () { return d3.transition() - .duration(500) + .duration(1500) .ease(d3.easeCubicInOut) } function getExitTransition() { return d3.transition() - .duration(500) + .duration(1500) .ease(d3.easeCubicInOut) } </script> -- GitLab From cdef8303e1e090c50f131f307009253bbccd6e16 Mon Sep 17 00:00:00 2001 From: Corson-Dosch <hcorson-dosch@usgs.gov> Date: Tue, 27 Aug 2024 17:37:18 -0500 Subject: [PATCH 08/25] add more less vulnerable labels --- src/components/FishAsFoodLinkChartViz.vue | 38 ++++++++++++++--------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/components/FishAsFoodLinkChartViz.vue b/src/components/FishAsFoodLinkChartViz.vue index 550bb66..b9e9c92 100644 --- a/src/components/FishAsFoodLinkChartViz.vue +++ b/src/components/FishAsFoodLinkChartViz.vue @@ -85,7 +85,7 @@ width: chart.value.offsetWidth, height: 1800, margin: 20, - marginBottom: 60, + marginBottom: 80, marginLeft: 200}); drawChart(data.value, scalePercent.value); @@ -359,20 +359,20 @@ .attr("dy", xAxisLabelDy) .style("text-anchor", "middle") .text(scalePercent ? 'Percent change in harvest-weighted climate vulnerability, 2030-2075' : 'Change in harvest-weighted climate vulnerability, 2030-2075') - // xAxis.append("text") - // .attr("class", "x-axis axis-title") - // .attr("x", 0) - // .attr("y", xAxisLabelYPosition * 4) - // .attr("dy", xAxisLabelDy) - // .style("text-anchor", "start") - // .text('Less vulnerable') - // xAxis.append("text") - // .attr("class", "x-axis axis-title") - // .attr("x", chartDimensions.boundedWidth) - // .attr("y", xAxisLabelYPosition * 4) - // .attr("dy", xAxisLabelDy) - // .style("text-anchor", "end") - // .text('More vulnerable') + xAxis.append("text") + .attr("class", "x-axis axis-subtitle") + .attr("x", 0) + .attr("y", xAxisLabelYPosition * 5.5) + .attr("dy", xAxisLabelDy) + .style("text-anchor", "start") + .text('Less vulnerable') + xAxis.append("text") + .attr("class", "x-axis axis-subtitle") + .attr("x", chartDimensions.boundedWidth) + .attr("y", xAxisLabelYPosition * 5.5) + .attr("dy", xAxisLabelDy) + .style("text-anchor", "end") + .text('More vulnerable') // Remove axix line and labels // xAxis.select(".domain").remove() @@ -676,4 +676,12 @@ fill: var(--color-text); user-select: none; } + .axis-subtitle { + font-size: 1.8rem; + font-family: var(--default-font); + font-weight: 400; + font-style: italic; + fill: var(--color-text); + user-select: none; + } </style> \ No newline at end of file -- GitLab From 4db3ae1cc3069055181d20d68692e1d2ea4d634c Mon Sep 17 00:00:00 2001 From: Corson-Dosch <hcorson-dosch@usgs.gov> Date: Wed, 28 Aug 2024 17:25:58 -0500 Subject: [PATCH 09/25] get family expansion working --- public/fish_as_food_climate.csv | 224 ++++++++++++-------- src/components/FishAsFoodLinkChartViz.vue | 243 +++++++++++++++------- 2 files changed, 312 insertions(+), 155 deletions(-) diff --git a/public/fish_as_food_climate.csv b/public/fish_as_food_climate.csv index a64a562..ab5c337 100644 --- a/public/fish_as_food_climate.csv +++ b/public/fish_as_food_climate.csv @@ -1,84 +1,142 @@ family,thermal_guild,species,cvi_2030,cvi_2075,cvi_2030_family,cvi_2075_family -Cyprinidae,warm,Albanian roach,0.10778174260000001,0.094120842,0.10778174260000001,0.094120842 -Cyprinidae,warm,Asp,0.013429605127960001,0.011727456938119999,0.013429605127960001,0.011727456938119999 -Cyprinidae,warm,Barbel,0.047058011,0.039834022,0.047058011,0.039834022 -Cyprinidae,warm,Bata,0.00551214,0.007563008,0.00551214,0.007563008 -Cyprinidae,warm,BeyÅŸehir bleak,7.267071073e-4,6.011377922000001e-4,7.267071073e-4,6.011377922000001e-4 -Cyprinidae,warm,Black bream,0.051246241799999995,0.061345053,0.051246241799999995,0.061345053 -Cyprinidae,warm,Black carp,0.051246241799999995,0.061345053,0.051246241799999995,0.061345053 -Cyprinidae,warm,Blue-finned mahseer,0.022459868,0.027820540914799997,0.022459868,0.027820540914799997 -Cyprinidae,warm,Catla,0.022074318,0.030287375,0.022074318,0.030287375 -Cyprinidae,warm,Chocolate mahseer,0.002830632,0.003430213,0.002830632,0.003430213 -Cyprinidae,warm,Chub,0.032161674,0.026581131487899996,0.032161674,0.026581131487899996 -Cyprinidae,warm,Common barbel,0.002834382,0.002399269,0.002834382,0.002399269 -Cyprinidae,warm,Common bleak,0.008549495,0.007072209,0.008549495,0.007072209 -Cyprinidae,warm,Common bream,0.046006207,0.036428658,0.046006207,0.036428658 -Cyprinidae,warm,Common carp,0.13811330662322036,0.1199294950838376,0.13811330662322036,0.1199294950838376 -Cyprinidae,warm,Common nase,0.026387573782000004,0.023642661213400005,0.026387573782000004,0.023642661213400005 -Cyprinidae,warm,Common roach,0.053126247,0.042622199,0.053126247,0.042622199 -Cyprinidae,warm,Crucian carp,0.116377215,0.101055148,0.116377215,0.101055148 -Cyprinidae,warm,Cyprinds,0.032788926,0.028471980271733335,0.032788926,0.028471980271733335 -Cyprinidae,warm,European carp,0.130406398,0.11323726735275001,0.130406398,0.11323726735275001 -Cyprinidae,warm,European chub,0.001917238,0.001584568,0.001917238,0.001584568 -Cyprinidae,warm,Ghonia,0.001828794,0.002509222,0.001828794,0.002509222 -Cyprinidae,warm,Golden mahseer,0.003435564,0.003913307,0.003435564,0.003913307 -Cyprinidae,warm,Goldfish,0.189583548,0.164623234,0.189583548,0.164623234 -Cyprinidae,warm,Grass carp,0.028623161305374997,0.034263768,0.028623161305374997,0.034263768 -Cyprinidae,warm,Gudgeon,0.034406967,0.030376054976399995,0.034406967,0.030376054976399995 -Cyprinidae,warm,Italian bleak,0.10233745969859999,0.084654346,0.10233745969859999,0.084654346 -Cyprinidae,warm,Kalibaus,0.003683346,0.005053786,0.003683346,0.005053786 -Cyprinidae,warm,Kutum,2.15702238e-4,1.9326426059999998e-4,2.15702238e-4,1.9326426059999998e-4 -Cyprinidae,warm,Labeo,0.017643999771499996,0.024208694942499998,0.017643999771499996,0.024208694942499998 -Cyprinidae,warm,Mrigal,0.019859158866899998,0.027248035,0.019859158866899998,0.027248035 -Cyprinidae,warm,Mullet,0.022048241277999998,0.018222534,0.022048241277999998,0.018222534 -Cyprinidae,warm,Orange River mudfish,0.018030365,0.024738812350000004,0.018030365,0.024738812350000004 -Cyprinidae,warm,Pale chub,0.002323163,0.002780976,0.002323163,0.002780976 -Cyprinidae,warm,Pearl mullet,0.177701261,0.14699587071620002,0.177701261,0.14699587071620002 -Cyprinidae,warm,Prussian carp,0.064146948,0.055701447,0.064146948,0.055701447 -Cyprinidae,warm,Prussian/Crucian carp,0.021666691,0.018814083880000002,0.021666691,0.018814083880000002 -Cyprinidae,warm,Razor fish (ziege),3.7723609910000005e-4,3.294229477e-4,3.7723609910000005e-4,3.294229477e-4 -Cyprinidae,warm,Red garra,0.017643999771499996,0.024208694942499998,0.017643999771499996,0.024208694942499998 -Cyprinidae,warm,Rohu,0.001751521,0.002403199,0.001751521,0.002403199 -Cyprinidae,warm,Rudd,0.010652671243479999,0.008811973,0.010652671243479999,0.008811973 -Cyprinidae,warm,Rui,0.036807702,0.050502518,0.036807702,0.050502518 -Cyprinidae,warm,Shabout,0.001199162,0.001015075,0.001199162,0.001015075 -Cyprinidae,warm,Silver bream,0.010815032339999999,0.008563564,0.010815032339999999,0.008563564 -Cyprinidae,warm,Silver carp,0.029425103982114283,0.035223745194000004,0.029425103982114283,0.035223745194000004 -Cyprinidae,warm,Tench,0.017184651140000003,0.015171401,0.017184651140000003,0.015171401 -Cyprinidae,warm,Transcau casian barb,0.014553462,0.012319325,0.014553462,0.012319325 -Cyprinidae,warm,Trout barb,0.004578617,0.003875743,0.004578617,0.003875743 -Cyprinidae,warm,Vimba bream,0.021286894,0.018588866,0.021286894,0.018588866 -Cyprinidae,warm,White Bream,0.043260129359999996,0.034254257760000004,0.043260129359999996,0.034254257760000004 -Percidae,cool,Eurasian Ruffe,0.004867533,0.00431991,0.004867533,0.00431991 -Percidae,cool,European perch,0.072594639,0.063470599,0.072594639,0.063470599 -Percidae,cool,Golden perch,0.033427689,0.034436023,0.033427689,0.034436023 -Percidae,cool,Redfin perch,0.026313675,0.023006446,0.026313675,0.023006446 -Percidae,cool,Volga pikeperch,8.01681972e-4,6.487112373e-4,8.01681972e-4,6.487112373e-4 -Percidae,cool,Walleye,0.10268783482319999,0.09835619,0.10268783482319999,0.09835619 -Percidae,cool,Yellow perch,0.048807665,0.045605899,0.048807665,0.045605899 -Percidae,cool,Zander (pikeperch),0.037962706268244786,0.032533836325300367,0.037962706268244786,0.032533836325300367 -Salmonidae,cold,Arctic Char,0.035655772,0.035000711,0.035655772,0.035000711 -Salmonidae,cold,Atlantic salmon,0.265177842,0.24011746662753772,0.265177842,0.24011746662753772 -Salmonidae,cold,Balkan trout,0.026710147320000002,0.023695444160000002,0.026710147320000002,0.023695444160000002 -Salmonidae,cold,Brook Trout,0.026461932224000003,0.024708687848000004,0.026461932224000003,0.024708687848000004 -Salmonidae,cold,Brown Trout,0.050732109690129576,0.045006112,0.050732109690129576,0.045006112 -Salmonidae,cold,Cherry trout,0.00293711,0.002762575,0.00293711,0.002762575 -Salmonidae,cold,Chinook Salmon (Quinnat Salmon),0.160075296,0.15222991949999998,0.160075296,0.15222991949999998 -Salmonidae,cold,Chum salmon,0.028850453,0.028417105,0.028850453,0.028417105 -Salmonidae,cold,Coho salmon,0.215094746,0.200664817,0.215094746,0.200664817 -Salmonidae,cold,Common huchen,5.120283490000001e-5,4.81393951e-5,5.120283490000001e-5,4.81393951e-5 -Salmonidae,cold,Dolly varden/Char,0.00347886,0.003358148,0.00347886,0.003358148 -Salmonidae,cold,European grayling,0.024065881522700002,0.021200949,0.024065881522700002,0.021200949 -Salmonidae,cold,Pink salmon,0.025550719900000005,0.024439884,0.025550719900000005,0.024439884 -Salmonidae,cold,Rainbow trout,0.06302419,0.059670455,0.06302419,0.059670455 -Salmonidae,cold,Salmon,0.064490204,0.062553346,0.064490204,0.062553346 -Salmonidae,cold,Sockeye salmon,0.029446763100000002,0.029350874649999997,0.029446763100000002,0.029350874649999997 -Salmonidae,cold,Steelhead,0.004928901,0.004666617,0.004928901,0.004666617 -Salmonidae,cold,Trout,0.08583577,0.08267911,0.08583577,0.08267911 -Salmonidae,cold,Vendace,0.041166224,0.038363594,0.041166224,0.038363594 -Salmonidae,cold,Whitefish,0.050076846,0.046667573,0.050076846,0.046667573 -Siluridae,warm,Boal,0.012433096,0.010511807,0.012433096,0.010511807 -Siluridae,warm,Catfish,0.033215868223999995,0.028083014042666665,0.033215868223999995,0.028083014042666665 -Siluridae,warm,European catfish,0.016684117,0.014105917603999998,0.016684117,0.014105917603999998 -Siluridae,warm,Far eastern catfish,0.003108274,0.002627952,0.003108274,0.002627952 -Siluridae,warm,Wels catfish,0.033343302,0.028190755380945457,0.033343302,0.028190755380945457 +Alestidae,warm,Big-scale tetra,0.012274708350000002,0.0108627154,0.11286009582760641,0.10272450171172955 +Alestidae,warm,Nurse tetra,0.012274708350000002,0.0108627154,0.11286009582760641,0.10272450171172955 +Alestidae,warm,Tigerfish,0.17991702081267735,0.1639656925862159,0.11286009582760641,0.10272450171172955 +Anguillidae,warm,Australian eel,0.0019279156693999997,0.0018898418147999998,0.060088577503724185,0.05356143600511973 +Anguillidae,warm,European eel,0.07434074572260523,0.06609150511026632,0.060088577503724185,0.05356143600511973 +Anguillidae,warm,Japanese eel,0.0036558991076,0.0042168184693999996,0.060088577503724185,0.05356143600511973 +Centrarchidae,warm,Bass,0.0657804855974,0.062753579817,0.06571650285962258,0.06199132366577771 +Centrarchidae,warm,Black bass,0.06640203166666667,0.0643960009,0.06571650285962258,0.06199132366577771 +Centrarchidae,warm,Crappie,0.0699718436,0.06477207332000001,0.06571650285962258,0.06199132366577771 +Centrarchidae,warm,Largemouth bass,0.07146540516942362,0.06686251627901083,0.06571650285962258,0.06199132366577771 +Centrarchidae,warm,Sunfish,0.0673828433562,0.062317355349150005,0.06571650285962258,0.06199132366577771 +Centrarchidae,warm,"White bass, striped bass",0.0386972223,0.0352063888,0.06571650285962258,0.06199132366577771 +Characidae,warm,Dorado,0.012366997772518243,0.011679929906890194,0.03036102208400994,0.028133410406297642 +Characidae,warm,Goldenfish,0.09620422520000001,0.08895133840000001,0.03036102208400994,0.028133410406297642 +Characidae,warm,Macabil,0.0801381195916,0.0740964648872,0.03036102208400994,0.028133410406297642 +Characidae,warm,Sabaleta,0.012333875025147671,0.011404017743133585,0.03036102208400994,0.028133410406297642 +Characidae,warm,Sabalo del PatÃa,0.012333875025147671,0.011404017743133585,0.03036102208400994,0.028133410406297642 +Characidae,warm,South American trout,0.004810211260000001,0.00444756692,0.03036102208400994,0.028133410406297642 +Characidae,warm,"Yamú, Bocón, Sábalo",0.012333875025147671,0.011404017743133585,0.03036102208400994,0.028133410406297642 +Cichlidae,warm,Bay snook (Tenguayaca),0.052899941706100004,0.0543490282988,0.020603395776181064,0.020246516151158973 +Cichlidae,warm,"Mojarra Plateada, Tilapia",0.01490390997376282,0.01381896607637032,0.020603395776181064,0.020246516151158973 +Cichlidae,warm,Mozambique tilapia,0.00698411364,0.00638055316,0.020603395776181064,0.020246516151158973 +Cichlidae,warm,Oscar,0.008136825204802732,0.008359717025306638,0.020603395776181064,0.020246516151158973 +Cichlidae,warm,Pavón,0.008136825204802732,0.008359717025306638,0.020603395776181064,0.020246516151158973 +Cichlidae,warm,Pavón Cinchado - Pinta de Lapa,0.007861983512506034,0.008050564307370287,0.020603395776181064,0.020246516151158973 +Cichlidae,warm,Pavón Mariposo,0.008136825204802732,0.008359717025306638,0.020603395776181064,0.020246516151158973 +Cichlidae,warm,Peacock Bass,0.0317336183,0.0326028964,0.020603395776181064,0.020246516151158973 +Cichlidae,warm,"Speckled pavon, speckled peacock bass (percid)",0.030661735699999998,0.031397200800000004,0.020603395776181064,0.020246516151158973 +Cichlidae,warm,Tilapia,0.0344154895108,0.031549818668399995,0.020603395776181064,0.020246516151158973 +Cichlidae,warm,Tilapia Negra,0.008953991845795688,0.00818019635864715,0.020603395776181064,0.020246516151158973 +Clariidae,warm,African catfish,0.012784239967400001,0.0122263953392,0.027590318372466668,0.02638640551306667 +Clariidae,warm,African sharptooth catfish,0.0466578101,0.044621880800000006,0.027590318372466668,0.02638640551306667 +Clariidae,warm,North African catfish,0.02332890505,0.022310940400000003,0.027590318372466668,0.02638640551306667 +Cyprinidae,warm,Albanian roach,0.10778174260000001,0.0941208422,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Asp,0.013429605127960001,0.011727456938119999,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Barbel,0.04705801130333334,0.039834021549,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Bata,0.0055121400746,0.007563008347000001,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,BeyÅŸehir bleak,7.267071073e-4,6.011377922000001e-4,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Black bream,0.051246241799999995,0.061345053,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Black carp,0.051246241799999995,0.061345053,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Blue-finned mahseer,0.0224598681704,0.027820540914799997,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Catla,0.0220743179623,0.0302873745485,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Chocolate mahseer,0.0028306322192,0.0034302133044,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Chub,0.03216167369029999,0.026581131487899996,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Common barbel,0.002834382148,0.0023992692516,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Common bleak,0.00854949538,0.007072209320000001,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Common bream,0.04600620713676522,0.03642865846998261,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Common carp,0.13811330662322036,0.1199294950838376,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Common nase,0.026387573782000004,0.023642661213400005,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Common roach,0.05312624687117,0.042622199202855,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Crucian carp,0.116377215108,0.10105514804045,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Cyprinds,0.032788926016,0.028471980271733335,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,European carp,0.13040639766,0.11323726735275001,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,European chub,0.001917238372,0.001584568196,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Ghonia,0.0018287941369,0.0025092223955,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Golden mahseer,0.0034355641156000004,0.0039133067584,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Goldfish,0.189583548,0.16462323395,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Grass carp,0.028623161305374997,0.034263768144375,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Gudgeon,0.0344069672688,0.030376054976399995,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Italian bleak,0.10233745969859999,0.0846543455604,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Kalibaus,0.0036833459377,0.0050537859515,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Kutum,2.15702238e-4,1.9326426059999998e-4,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Labeo,0.017643999771499996,0.024208694942499998,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Mrigal,0.019859158866899998,0.0272480347455,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Mullet,0.022048241277999998,0.018222534254,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Orange River mudfish,0.01803036473,0.024738812350000004,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Pale chub,0.0023231629616,0.0027809757360000003,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Pearl mullet,0.1777012614733,0.14699587071620002,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Prussian carp,0.064146947634,0.055701447087225,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Prussian/Crucian carp,0.0216666912,0.018814083880000002,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Razor fish (ziege),3.7723609910000005e-4,3.294229477e-4,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Red garra,0.017643999771499996,0.024208694942499998,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Rohu,0.0017515211452,0.0024031989140000003,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Rudd,0.010652671243479999,0.00881197281272,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Rui,0.0368077017131,0.0505025183545,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Shabout,0.0011991616780000001,0.0010150754526,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Silver bream,0.010815032339999999,0.008563564440000001,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Silver carp,0.029425103982114283,0.035223745194000004,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Tench,0.017184651140000003,0.015171401295,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Transcau casian barb,0.014553462183,0.0123193248111,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Trout barb,0.004578617316,0.0038757426371999995,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,Vimba bream,0.0212868941635,0.0185888663345,0.05987770694593892,0.052805491729310206 +Cyprinidae,warm,White Bream,0.043260129359999996,0.034254257760000004,0.05987770694593892,0.052805491729310206 +Percidae,cool,Eurasian Ruffe,0.00486753292,0.0043199098100000005,0.053766659159614545,0.04716731515478815 +Percidae,cool,European perch,0.07259463927767063,0.06347059881833862,0.053766659159614545,0.04716731515478815 +Percidae,cool,Golden perch,0.0334276893426,0.0344360226346,0.053766659159614545,0.04716731515478815 +Percidae,cool,Redfin perch,0.0263136745872,0.0230064464784,0.053766659159614545,0.04716731515478815 +Percidae,cool,Volga pikeperch,8.01681972e-4,6.487112373e-4,0.053766659159614545,0.04716731515478815 +Percidae,cool,Walleye,0.10268783482319999,0.0983561899176,0.053766659159614545,0.04716731515478815 +Percidae,cool,Yellow perch,0.0488076651,0.0456058989,0.053766659159614545,0.04716731515478815 +Percidae,cool,Zander (pikeperch),0.037962706268244786,0.032533836325300367,0.053766659159614545,0.04716731515478815 +Pimelodidae,warm,"Amarillo, Toruno, Chontaduro",0.007525378768929754,0.007699380717640743,0.010981231987585237,0.011153188357481665 +Pimelodidae,warm,"Bagre, Bagre Rayado",0.007525378768929754,0.007699380717640743,0.010981231987585237,0.011153188357481665 +Pimelodidae,warm,Barbiancho,0.008066403358651704,0.00795324982019469,0.010981231987585237,0.011153188357481665 +Pimelodidae,warm,Barred sorubim,0.02347918176,0.02402206784,0.010981231987585237,0.011153188357481665 +Pimelodidae,warm,Blancopobre,0.007525378768929754,0.007699380717640743,0.010981231987585237,0.011153188357481665 +Pimelodidae,warm,Blanquillo,0.007525378768929754,0.007699380717640743,0.010981231987585237,0.011153188357481665 +Pimelodidae,warm,Cajaro,0.008066403358651704,0.00795324982019469,0.010981231987585237,0.011153188357481665 +Pimelodidae,warm,Cucharo,0.007525378768929754,0.007699380717640743,0.010981231987585237,0.011153188357481665 +Pimelodidae,warm,Flatwhiskered catfish,0.00629179462,0.006203534859999999,0.010981231987585237,0.011153188357481665 +Pimelodidae,warm,Gilded catfish,0.007525378768929754,0.007699380717640743,0.010981231987585237,0.011153188357481665 +Pimelodidae,warm,Kumakuma (catfish),0.0293489772,0.030027584800000003,0.010981231987585237,0.011153188357481665 +Pimelodidae,warm,Pati,0.00629179462,0.006203534859999999,0.010981231987585237,0.011153188357481665 +Pimelodidae,warm,"Pintadillo, Bagre, Bagre Rayado",0.007525378768929754,0.007699380717640743,0.010981231987585237,0.011153188357481665 +Pimelodidae,warm,Redtail catfish,0.00629179462,0.006203534859999999,0.010981231987585237,0.011153188357481665 +Pimelodidae,warm,South American catfish,0.0293489772,0.030027584800000003,0.010981231987585237,0.011153188357481665 +Pimelodidae,warm,Spotted sorubim,0.0293489772,0.030027584800000003,0.010981231987585237,0.011153188357481665 +Pimelodidae,warm,"Tigrito, tijero",0.007525378768929754,0.007699380717640743,0.010981231987585237,0.011153188357481665 +Pimelodidae,warm,"Valentón, Plumita",0.007525378768929754,0.007699380717640743,0.010981231987585237,0.011153188357481665 +Pimelodidae,warm,White catfish,0.00629179462,0.006203534859999999,0.010981231987585237,0.011153188357481665 +Pimelodidae,warm,Yellow catfish,0.009437691929999999,0.00930530229,0.010981231987585237,0.011153188357481665 +Salmonidae,cold,Arctic Char,0.0356557719054,0.03500071147903334,0.06944668713248901,0.06438766096040704 +Salmonidae,cold,Atlantic salmon,0.265177841604771,0.24011746662753772,0.06944668713248901,0.06438766096040704 +Salmonidae,cold,Balkan trout,0.026710147320000002,0.023695444160000002,0.06944668713248901,0.06438766096040704 +Salmonidae,cold,Brook Trout,0.026461932224000003,0.024708687848000004,0.06944668713248901,0.06438766096040704 +Salmonidae,cold,Brown Trout,0.050732109690129576,0.04500611164287132,0.06944668713248901,0.06438766096040704 +Salmonidae,cold,Cherry trout,0.0029371095160000004,0.0027625745076,0.06944668713248901,0.06438766096040704 +Salmonidae,cold,Chinook Salmon (Quinnat Salmon),0.1600752957,0.15222991949999998,0.06944668713248901,0.06438766096040704 +Salmonidae,cold,Chum salmon,0.02885045315,0.0284171047,0.06944668713248901,0.06438766096040704 +Salmonidae,cold,Coho salmon,0.2150947458,0.20066481688,0.06944668713248901,0.06438766096040704 +Salmonidae,cold,Common huchen,5.120283490000001e-5,4.81393951e-5,0.06944668713248901,0.06438766096040704 +Salmonidae,cold,Dolly varden/Char,0.0034788601644,0.0033581483528,0.06944668713248901,0.06438766096040704 +Salmonidae,cold,European grayling,0.024065881522700002,0.0212009485031,0.06944668713248901,0.06438766096040704 +Salmonidae,cold,Pink salmon,0.025550719900000005,0.02443988385,0.06944668713248901,0.06438766096040704 +Salmonidae,cold,Rainbow trout,0.06302419030074105,0.05967045472608766,0.06944668713248901,0.06438766096040704 +Salmonidae,cold,Salmon,0.06449020412644443,0.06255334611855555,0.06944668713248901,0.06438766096040704 +Salmonidae,cold,Sockeye salmon,0.029446763100000002,0.029350874649999997,0.06944668713248901,0.06438766096040704 +Salmonidae,cold,Steelhead,0.0049289009400000005,0.00466661704,0.06944668713248901,0.06438766096040704 +Salmonidae,cold,Trout,0.0858357697255,0.08267910955158334,0.06944668713248901,0.06438766096040704 +Salmonidae,cold,Vendace,0.04116622401,0.0383635935,0.06944668713248901,0.06438766096040704 +Salmonidae,cold,Whitefish,0.05007684609903145,0.04666757308221,0.06944668713248901,0.06438766096040704 +Serrasalmidae,warm,"Cachama, Morocoto",0.008435958589406153,0.008389041102228541,0.023958122394703078,0.02382487673111427 +Serrasalmidae,warm,"Cachama, Pacú",0.008435958589406153,0.008389041102228541,0.023958122394703078,0.02382487673111427 +Serrasalmidae,warm,"Cachara, Tambaqui",0.055930405450000005,0.05561934251,0.023958122394703078,0.02382487673111427 +Serrasalmidae,warm,"Cherna, Cachama Negra",0.008435958589406153,0.008389041102228541,0.023958122394703078,0.02382487673111427 +Serrasalmidae,warm,Pacu,0.031255226575,0.031081397285000006,0.023958122394703078,0.02382487673111427 +Siluridae,warm,Boal,0.0124330956288,0.0105118070912,0.0286296405472,0.024205496966133333 +Siluridae,warm,Catfish,0.033215868223999995,0.028083014042666665,0.0286296405472,0.024205496966133333 +Siluridae,warm,European catfish,0.016684117296,0.014105917603999998,0.0286296405472,0.024205496966133333 +Siluridae,warm,Far eastern catfish,0.0031082739072,0.0026279517728,0.0286296405472,0.024205496966133333 +Siluridae,warm,Wels catfish,0.0333433019136,0.028190755380945457,0.0286296405472,0.024205496966133333 diff --git a/src/components/FishAsFoodLinkChartViz.vue b/src/components/FishAsFoodLinkChartViz.vue index b9e9c92..2846298 100644 --- a/src/components/FishAsFoodLinkChartViz.vue +++ b/src/components/FishAsFoodLinkChartViz.vue @@ -11,7 +11,6 @@ <template #aboveExplanation> </template> <template #figures> - <div id="chart-container" class="maxWidth" ref="chart"></div> <button aria-pressed="!scalePercent" class="button" @@ -20,6 +19,7 @@ > {{ scaleType }} </button> + <div id="chart-container" class="maxWidth" ref="chart"></div> </template> <!-- FIGURE CAPTION --> <template #figureCaption> @@ -31,7 +31,7 @@ </template> <script setup> - import { computed, onMounted, ref } from "vue"; //, reactive + import { computed, onMounted, reactive, ref } from "vue"; //, reactive import * as d3 from 'd3'; import VizSection from '@/components/VizSection.vue'; @@ -44,6 +44,7 @@ const publicPath = import.meta.env.BASE_URL; const dataFile = 'fish_as_food_climate.csv' //'fish_as_food_climate_test.csv' const data = ref(); + const families = ref(); const chart = ref(null); const scalePercent = ref(false); let chartDimensions; @@ -51,13 +52,13 @@ let chartSVG; let chartBounds; let xScale; - let xAxis; + let xAxisBottom; let yScale; let yAxis; let widthScale; let colorScale; // Create a reactive object to track expanded families - // const expandedFamilies = reactive({}); + const expandedFamilies = reactive({}); // set up filtered chart data as computed property const scaleType = computed(() => { @@ -71,15 +72,13 @@ try { await loadDatasets(); if (data.value.length > 0) { - // families.value = Array.from(new Set(data.value.map(d => d.family))); - // // Initialize the expandedFamilies object - // families.value.forEach(family => { - // expandedFamilies[family] = false; - // }); - // expandedFamilies["A"] = !expandedFamilies["A"] - // console.log(expandedFamilies) - // Set starting scale type - // scalePercent.value = true; + families.value = Array.from(new Set(data.value.map(d => d.family))); + // Initialize the expandedFamilies object + families.value.forEach(family => { + expandedFamilies[family] = false; + }); + // expandedFamilies["Cyprinidae"] = !expandedFamilies["Cyprinidae"] + expandedFamilies["Salmonidae"] = !expandedFamilies["Salmonidae"] initChart({ width: chart.value.offsetWidth, @@ -129,7 +128,7 @@ function initChart({ width = 500, // outer width, in pixels - height = width, // outer height, in pixels + // height = width, // outer height, in pixels margin = 1, // default margins marginTop = margin, // top margin, in pixels marginBottom = margin, // left margin, in pixels @@ -139,7 +138,7 @@ // set up global chart dimensions chartDimensions = { width, - height, + //height, margin: { top: marginTop, right: marginRight, @@ -148,12 +147,12 @@ } } chartDimensions.boundedWidth = chartDimensions.width - chartDimensions.margin.left - chartDimensions.margin.right - chartDimensions.boundedHeight = chartDimensions.height - chartDimensions.margin.top - chartDimensions.margin.bottom + //chartDimensions.boundedHeight = chartDimensions.height - chartDimensions.margin.top - chartDimensions.margin.bottom // draw canvas for chart chartSVG = d3.select("#chart-container") .append("svg") - .attr("viewBox", [0, 0, (chartDimensions.width), (chartDimensions.height)].join(' ')) + //.attr("viewBox", [0, 0, (chartDimensions.width), (chartDimensions.height)].join(' ')) .attr("width", "100%") .attr("height", "100%") .attr("id", "chart-svg") @@ -172,6 +171,10 @@ chartDimensions.margin.top }px)`) + // Initialize axes + initXAxis() + initYAxis() + // Initialize scales initXScale() initYScale() @@ -186,27 +189,30 @@ .attr("class", "points_2030") chartBounds.append("g") .attr("class", "points_2075") + chartBounds.append("g") + .attr("class", "rects") } function initXScale() { // scale for the x axis (domain set in `drawChart()`) xScale = d3.scaleLinear() .range([0, chartDimensions.boundedWidth]); - + } + function initXAxis() { // add group for x axis - xAxis = chartBounds.append("g") + xAxisBottom = chartBounds.append("g") .attr("id", "x-axis") .attr("class", "axis") - .attr("transform", `translate(0,${chartDimensions.boundedHeight})`) + //.attr("transform", `translate(0,${chartDimensions.boundedHeight})`) .attr("aria-hidden", true) // hide from screen reader // generate x axis - // xAxis + // xAxisBottom // .call(d3.axisBottom(xScale).tickSize(0).tickPadding(10)) // .select(".domain").remove() // remove axis line // // add placeholder for x axis title (title text set in drawChart()) - // xAxis + // xAxisBottom // .append("text") // .attr("class", "x-axis axis-title") // .attr("x", -chartDimensions.boundedWidth / 2) @@ -218,9 +224,12 @@ function initYScale() { // scale for y axis (domain set in `drawChart()`) yScale = d3.scaleBand() - .range([chartDimensions.boundedHeight, 0]) + // .range([chartDimensions.boundedHeight, 0]) .padding(0.1) + + } + function initYAxis() { // add group for y axis yAxis = chartBounds.append("g") .attr("id", "y-axis") @@ -312,7 +321,7 @@ function drawChart(data, scalePercent) { // accessor functions - const yAccessor = d => d.species + const yAccessor = d => expandedFamilies[d.family] ? d.species : d.family //d.species // const yAccessor_family = d => d.family const xAccessor = d => d.cvi // const xAccessor_family = d => d.cvi_family @@ -322,7 +331,7 @@ const x1Accessor_family = d => scalePercent ? (d.cvi_2075_family - d.cvi_2030_family)/ d.cvi_2030_family : d.cvi_2075_family const widthAccessor = d => d.position const colorAccessor = d => d.thermal_guild - const identifierAccessor = d => d.family + '_' + d.species.replace(/ /g,"_"); + const identifierAccessor = d => expandedFamilies[d.family] ? d.family + '_' + d.species.replace(/ /g,"_") : d.family; // to get dynamic // need key for data @@ -343,65 +352,82 @@ .nice() } - xAxis.transition(getUpdateTransition()) + xAxisBottom.transition(getUpdateTransition()) .call(d3.axisBottom(xScale).tickSize(0).tickPadding(10)); - xAxis + xAxisBottom .selectAll("text") .attr("class", "axis-text") - const xAxisLabelYPosition = xAxis.select("text").attr('y') - const xAxisLabelDy = xAxis.select("text").attr('dy') - xAxis.append("text") + const xAxisBottomLabelYPosition = xAxisBottom.select("text").attr('y') + const xAxisBottomLabelDy = xAxisBottom.select("text").attr('dy') + xAxisBottom.append("text") .attr("class", "x-axis axis-title") .attr("x", chartDimensions.boundedWidth / 2) - .attr("y", xAxisLabelYPosition * 4) - .attr("dy", xAxisLabelDy) + .attr("y", xAxisBottomLabelYPosition * 4) + .attr("dy", xAxisBottomLabelDy) .style("text-anchor", "middle") .text(scalePercent ? 'Percent change in harvest-weighted climate vulnerability, 2030-2075' : 'Change in harvest-weighted climate vulnerability, 2030-2075') - xAxis.append("text") + xAxisBottom.append("text") .attr("class", "x-axis axis-subtitle") .attr("x", 0) - .attr("y", xAxisLabelYPosition * 5.5) - .attr("dy", xAxisLabelDy) + .attr("y", xAxisBottomLabelYPosition * 5.5) + .attr("dy", xAxisBottomLabelDy) .style("text-anchor", "start") .text('Less vulnerable') - xAxis.append("text") + xAxisBottom.append("text") .attr("class", "x-axis axis-subtitle") .attr("x", chartDimensions.boundedWidth) - .attr("y", xAxisLabelYPosition * 5.5) - .attr("dy", xAxisLabelDy) + .attr("y", xAxisBottomLabelYPosition * 5.5) + .attr("dy", xAxisBottomLabelDy) .style("text-anchor", "end") .text('More vulnerable') // Remove axix line and labels - // xAxis.select(".domain").remove() - // xAxis.call(d3.axisBottom(xScale).tickValues([])) + // xAxisBottom.select(".domain").remove() + // xAxisBottom.call(d3.axisBottom(xScale).tickValues([])) // set domain for yScale - // let yDomain = [] - // families.value.map(family => expandedFamilies[family] ? yDomain.push(data.filter(d => d.family === family).map(d => d.species)) : yDomain.push(family)) - // yDomain = yDomain.flat() - // yScale - // .domain(yDomain) + let yDomain = [] + families.value.map(family => expandedFamilies[family] ? yDomain.push(data.filter(d => d.family === family).map(d => d.species)) : yDomain.push(family)) + yDomain = yDomain.flat() + + const totalHeight = yDomain.length * 25; + chartDimensions.boundedHeight = totalHeight + chartDimensions.height = chartDimensions.boundedHeight + chartDimensions.margin.top + chartDimensions.margin.bottom + chartSVG + .transition(getUpdateTransition()) + .attr("viewBox", [0, 0, (chartDimensions.width), (chartDimensions.height)].join(' ')) + + // Set range and domain for y scale yScale - .domain(d3.union(data.map(yAccessor))) //.sort(d3.ascending))) + .range([chartDimensions.boundedHeight, 0]) + .domain(yDomain) + //yScale + // .domain(d3.union(data.map(yAccessor))) //.sort(d3.ascending))) + + // set y position for xAxisBottom + xAxisBottom + .transition(getUpdateTransition()) + .attr("transform", `translate(0,${chartDimensions.boundedHeight})`) + yAxis + .transition(getUpdateTransition()) .call(d3.axisLeft(yScale).tickSize(2)) yAxis .selectAll("text") - .attr("class", "axis-text") + .attr("class", d => expandedFamilies[d] === false ? "axis-text family" : "axis-text species") const yAxisLabelXPosition = yAxis.select("text").attr('x') const yAxisLabelDx = yAxis.select("text").attr('dx') - yAxis.append("text") - .attr("class", "y-axis axis-title") - .attr("y", 0) - .attr("x", yAxisLabelXPosition) - .attr("dx", yAxisLabelDx) - .style("text-anchor", "end") - .text('Species') + // yAxis.append("text") + // .attr("class", "y-axis axis-title") + // .attr("y", 0) + // .attr("x", yAxisLabelXPosition) + // .attr("dx", yAxisLabelDx) + // .style("text-anchor", "end") + // .text('Species') // set up width scale const radiusPosition0 = 1 @@ -424,26 +450,33 @@ initColorScale(colorCategories) // set up area data - const areaCategories = Array.from(new Set(data.map(yAccessor))) + const areaCategories = Array.from(new Set(data.map(d => d.species))) //Array.from(new Set(data.map(yAccessor))) const areaData = areaCategories.map(areaCategory => { + const species = areaCategory //expandedFamilies[areaCategory] === false ? null : areaCategory; + const family = data.filter(d => d.species === species)[0].family //expandedFamilies[areaCategory] === false ? areaCategory : data.filter(d => d.species === species)[0].family; + const cvi_2030 = expandedFamilies[family] ? x0Accessor(data.filter(d => d.species === species)[0]) : x0Accessor_family(data.filter(d => d.family === family)[0]) //species ? x0Accessor(data.filter(d => d.species === species)[0]) : x0Accessor_family(data.filter(d => d.family === family)[0]) + const cvi_2075 = expandedFamilies[family] ? x1Accessor(data.filter(d => d.species === species)[0]) : x1Accessor_family(data.filter(d => d.family === family)[0]) + const cvi_decreasing = cvi_2030 > cvi_2075 + const thermal_guild = colorAccessor(data.filter(d => d.species === species)[0]) //species ? colorAccessor(data.filter(d => d.species === species)[0]) : colorAccessor(data.filter(d => d.family === family)[0]) + return [ { - species: areaCategory, - family: data.filter(d => d.species === areaCategory)[0].family, - cvi: x0Accessor(data.filter(d => d.species === areaCategory)[0]), - cvi_decreasing: x0Accessor(data.filter(d => d.species === areaCategory)[0]) > x1Accessor(data.filter(d => d.species === areaCategory)[0]), - cvi_family: x0Accessor_family(data.filter(d => d.species === areaCategory)[0]), + species: species, + family: family, + cvi: cvi_2030, + cvi_decreasing: cvi_decreasing, + // cvi_family: x0Accessor_family(data.filter(d => d.species === areaCategory)[0]), position: 0, - thermal_guild: colorAccessor(data.filter(d => d.species === areaCategory)[0]) + thermal_guild: thermal_guild }, { - species: areaCategory, - family: data.filter(d => d.species === areaCategory)[0].family, - cvi: x1Accessor(data.filter(d => d.species === areaCategory)[0]), - cvi_decreasing: x0Accessor(data.filter(d => d.species === areaCategory)[0]) > x1Accessor(data.filter(d => d.species === areaCategory)[0]), - cvi_family: x1Accessor_family(data.filter(d => d.species === areaCategory)[0]), + species: species, + family: family, + cvi: cvi_2075, + cvi_decreasing: cvi_decreasing, + // cvi_family: x1Accessor_family(data.filter(d => d.species === areaCategory)[0]), position: 1, - thermal_guild: colorAccessor(data.filter(d => d.species === areaCategory)[0]) + thermal_guild: thermal_guild } ] }) @@ -452,7 +485,7 @@ // Enter-update-exit pattern for areas let areaGroups = chartBounds.selectAll(".areas") .selectAll(".area") - .data(areaData, d => d[0].species) + .data(areaData, d => d[0].species + d[0].family) const oldAreaGroups = areaGroups.exit() @@ -535,7 +568,7 @@ .append("circle") .attr("id", d => 'point-2030-' + identifierAccessor(d)) .attr("class", "point_2030") - .attr("cx", d => xScale(x0Accessor(d))) + .attr("cx", d => expandedFamilies[d.family] ? xScale(x0Accessor(d)) : xScale(x0Accessor_family(d))) .attr("cy", d => yScale(yAccessor(d)) + yScale.bandwidth() / 2) .attr("r", radiusPosition0) .style("stroke", d => colorScale(colorAccessor(d))) @@ -550,7 +583,7 @@ allPoints2030.transition(getUpdateTransition()) .attr("id", d => 'point-2030-' + identifierAccessor(d)) .attr("class", "point_2030") - .attr("cx", d => xScale(x0Accessor(d))) + .attr("cx", d => expandedFamilies[d.family] ? xScale(x0Accessor(d)) : xScale(x0Accessor_family(d))) .attr("cy", d => yScale(yAccessor(d)) + yScale.bandwidth() / 2) .attr("r", radiusPosition0) .style("stroke", d => colorScale(colorAccessor(d))) @@ -593,7 +626,7 @@ .append("circle") .attr("id", d => 'point-2075-' + identifierAccessor(d)) .attr("class", "point_2075") - .attr("cx", d => xScale(x1Accessor(d))) + .attr("cx", d => expandedFamilies[d.family] ? xScale(x1Accessor(d)) : xScale(x1Accessor_family(d))) .attr("cy", d => yScale(yAccessor(d)) + yScale.bandwidth() / 2) .attr("r", radiusPosition1 - strokeWidth1 / 2) .style("stroke", d => colorScale(colorAccessor(d))) @@ -609,7 +642,7 @@ allPoints2075.transition(getUpdateTransition()) .attr("id", d => 'point-2075-' + identifierAccessor(d)) .attr("class", "point_2075") - .attr("cx", d => xScale(x1Accessor(d))) + .attr("cx", d => expandedFamilies[d.family] ? xScale(x1Accessor(d)) : xScale(x1Accessor_family(d))) .attr("cy", d => yScale(yAccessor(d)) + yScale.bandwidth() / 2) .attr("r", radiusPosition1 - strokeWidth1 / 2) .style("stroke", d => colorScale(colorAccessor(d))) @@ -649,6 +682,65 @@ // .style("stroke", 'none') // .style("fill", "black") + // Enter-Update-Exit pattern for overlay rectangles + let rectGroups = chartBounds.selectAll('.rects') + .selectAll(".rect") + .data(data, d => d.species) + + const oldRectGroups = rectGroups.exit() + + oldRectGroups.selectAll('rect') + .transition(getExitTransition()) + .style("opacity", 0) + + oldRectGroups.transition(getExitTransition()).remove() + + const newRectGroups = rectGroups.enter().append("g") + .attr("class", d => "rect " + d.species) + .attr("id", d => 'rect-group-' + identifierAccessor(d)) + + // append points + newRectGroups + .append("rect") + .attr("id", d => 'rect-' + identifierAccessor(d)) + .attr("class", "rect") + .attr("x", -chartDimensions.margin.left) + .attr("y", d => yScale(yAccessor(d))) + .attr("height", yScale.bandwidth()) + .attr("width", chartDimensions.width) + .style("fill", "transparent") + + // update pointGroups2075 to include new points + rectGroups = newRectGroups.merge(rectGroups) + + const allRects = rectGroups.select("rect") + + // Update points based on data values + allRects.transition(getUpdateTransition()) + .attr("id", d => 'rect-' + identifierAccessor(d)) + .attr("class", "rect") + .attr("x", -chartDimensions.margin.left) + .attr("y", d => yScale(yAccessor(d))) + .attr("height", yScale.bandwidth()) + .attr("width", chartDimensions.width) + .style("fill", "transparent") + + allRects + .on("click", function(event, d) { + const clickedFamily = d.family + expandedFamilies[clickedFamily] = !expandedFamilies[clickedFamily] + // let yDomain = [] + // families.value.map(family => expandedFamilies[family] ? yDomain.push(data.filter(d => d.family === family).map(d => d.species)) : yDomain.push(family)) + // yDomain = yDomain.flat() + // const yBandwidth = yScale.bandwidth() + // chartDimensions.boundedHeight = yBandwidth * yDomain.length; + // chartDimensions.height = chartDimensions.boundedHeight + chartDimensions.margin.top + chartDimensions.margin.bottom; + // chartSVG.attr("viewBox", [0, 0, (chartDimensions.width), (chartDimensions.height)].join(' ')) + // yScale.range([chartDimensions.boundedHeight * 2, 0]) + // yAxis + // .call(d3.axisLeft(yScale).tickSize(2)) + drawChart(data, scalePercent) + }); } function getUpdateTransition () { @@ -668,6 +760,13 @@ font-size: 1.8rem; font-family: var(--default-font); user-select: none; + + } + .species { + font-style: italic; + } + .family { + font-weight: 900; } .axis-title { font-size: 1.8rem; -- GitLab From a95b400caa40852514ad57fd87383a836fbd3b01 Mon Sep 17 00:00:00 2001 From: Corson-Dosch <hcorson-dosch@usgs.gov> Date: Wed, 28 Aug 2024 17:29:17 -0500 Subject: [PATCH 10/25] Comment out unused variables --- src/components/FishAsFoodLinkChartViz.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/FishAsFoodLinkChartViz.vue b/src/components/FishAsFoodLinkChartViz.vue index 2846298..3c8c272 100644 --- a/src/components/FishAsFoodLinkChartViz.vue +++ b/src/components/FishAsFoodLinkChartViz.vue @@ -419,8 +419,8 @@ .selectAll("text") .attr("class", d => expandedFamilies[d] === false ? "axis-text family" : "axis-text species") - const yAxisLabelXPosition = yAxis.select("text").attr('x') - const yAxisLabelDx = yAxis.select("text").attr('dx') + // const yAxisLabelXPosition = yAxis.select("text").attr('x') + // const yAxisLabelDx = yAxis.select("text").attr('dx') // yAxis.append("text") // .attr("class", "y-axis axis-title") // .attr("y", 0) -- GitLab From 8ab69ba67c1d78ede73dd4813903d7c1be2dc40f Mon Sep 17 00:00:00 2001 From: Corson-Dosch <hcorson-dosch@usgs.gov> Date: Tue, 3 Sep 2024 14:21:06 -0500 Subject: [PATCH 11/25] add thumbnail and key message --- src/assets/content/ChartGrid.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/assets/content/ChartGrid.js b/src/assets/content/ChartGrid.js index 0deb495..0ca0bf6 100644 --- a/src/assets/content/ChartGrid.js +++ b/src/assets/content/ChartGrid.js @@ -25,9 +25,9 @@ export default { project: 'Fish as Food', vizKey: 'FishAsFoodLinkChart', vizRoute: 'inland-rec-fish-climate', - img_src: 'Placeholder_thumbnail.PNG', + img_src: 'FishAsFoodLinkChart_thumbnail.png', alt: '', - description: 'Description or key takeaways from this Viz (Viz 3)' + description: '...and recreationally fished inland inland fish species are vulnerable' }, { title: 'Glacier/Topography D3 Cross-Section Scan', -- GitLab From b5492e13b09121f75155e3538445a42a6477666f Mon Sep 17 00:00:00 2001 From: Corson-Dosch <hcorson-dosch@usgs.gov> Date: Tue, 3 Sep 2024 14:36:12 -0500 Subject: [PATCH 12/25] Add draft text, explainer, and prompt --- src/assets/text/text.js | 6 ++++++ src/components/FishAsFoodLinkChartViz.vue | 26 +++++++++++++++-------- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/assets/text/text.js b/src/assets/text/text.js index ec4aa29..e62ec9b 100644 --- a/src/assets/text/text.js +++ b/src/assets/text/text.js @@ -94,6 +94,12 @@ export default { 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.", paragraph2: "The total economic value for each species in each country is calculated by multiplying the total kilograms of bimoass harvested for each species by the price per kilogram, in U.S. dollars. Species- and country-specific price data were collected from November 2021 to February 2022." + }, + FishAsFoodLinkChart: { + paragraph1: 'Climate vulnerability index.', + explainerPart1: 'Showing the ', + explainerPart2: 'from 2030 to 2075 in harvest-weighted climate vulnerability under representative concentration pathway 4.5.', + prompt1: 'Click on the chart to show or hide <i>species</i> within each recreationally fished <b>family</b>' } } } \ No newline at end of file diff --git a/src/components/FishAsFoodLinkChartViz.vue b/src/components/FishAsFoodLinkChartViz.vue index 3c8c272..cc51207 100644 --- a/src/components/FishAsFoodLinkChartViz.vue +++ b/src/components/FishAsFoodLinkChartViz.vue @@ -9,16 +9,24 @@ </template> <!-- FIGURES --> <template #aboveExplanation> + <p v-html="text.paragraph1" /> + <p> + {{ text.explainerPart1 }} + <span> + <button + aria-pressed="!scalePercent" + class="button" + :text="scaleType" + @click="toggleScale" + > + {{ scaleType }} + </button> + </span> + {{ text.explainerPart2 }} + </p> + <p v-html="text.prompt1" /> </template> <template #figures> - <button - aria-pressed="!scalePercent" - class="button" - :text="scaleType" - @click="toggleScale" - > - {{ scaleType }} - </button> <div id="chart-container" class="maxWidth" ref="chart"></div> </template> <!-- FIGURE CAPTION --> @@ -62,7 +70,7 @@ // set up filtered chart data as computed property const scaleType = computed(() => { - return scalePercent.value ? 'Percent change' : 'Change' + return scalePercent.value ? 'percent change' : 'change' }); -- GitLab From af4f8b9df973a66f69dfc573def06c1d83a10577 Mon Sep 17 00:00:00 2001 From: Corson-Dosch <hcorson-dosch@usgs.gov> Date: Tue, 3 Sep 2024 23:10:22 -0500 Subject: [PATCH 13/25] simplify species name --- public/fish_as_food_climate.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/fish_as_food_climate.csv b/public/fish_as_food_climate.csv index ab5c337..bb9e48f 100644 --- a/public/fish_as_food_climate.csv +++ b/public/fish_as_food_climate.csv @@ -26,7 +26,7 @@ Cichlidae,warm,Pavón,0.008136825204802732,0.008359717025306638,0.02060339577618 Cichlidae,warm,Pavón Cinchado - Pinta de Lapa,0.007861983512506034,0.008050564307370287,0.020603395776181064,0.020246516151158973 Cichlidae,warm,Pavón Mariposo,0.008136825204802732,0.008359717025306638,0.020603395776181064,0.020246516151158973 Cichlidae,warm,Peacock Bass,0.0317336183,0.0326028964,0.020603395776181064,0.020246516151158973 -Cichlidae,warm,"Speckled pavon, speckled peacock bass (percid)",0.030661735699999998,0.031397200800000004,0.020603395776181064,0.020246516151158973 +Cichlidae,warm,Speckled pavon,0.030661735699999998,0.031397200800000004,0.020603395776181064,0.020246516151158973 Cichlidae,warm,Tilapia,0.0344154895108,0.031549818668399995,0.020603395776181064,0.020246516151158973 Cichlidae,warm,Tilapia Negra,0.008953991845795688,0.00818019635864715,0.020603395776181064,0.020246516151158973 Clariidae,warm,African catfish,0.012784239967400001,0.0122263953392,0.027590318372466668,0.02638640551306667 -- GitLab From 94cfca8b9caac35a31d63af21891e39cf8df9e19 Mon Sep 17 00:00:00 2001 From: Corson-Dosch <hcorson-dosch@usgs.gov> Date: Tue, 3 Sep 2024 23:10:42 -0500 Subject: [PATCH 14/25] tweak prompt text --- 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 e62ec9b..42ae131 100644 --- a/src/assets/text/text.js +++ b/src/assets/text/text.js @@ -99,7 +99,7 @@ export default { paragraph1: 'Climate vulnerability index.', explainerPart1: 'Showing the ', explainerPart2: 'from 2030 to 2075 in harvest-weighted climate vulnerability under representative concentration pathway 4.5.', - prompt1: 'Click on the chart to show or hide <i>species</i> within each recreationally fished <b>family</b>' + prompt1: 'Click on the chart to show or hide data for <i>species</i> within each <b>family</b>' } } } \ No newline at end of file -- GitLab From 95e1e672ac8a81a027b013b4d12c48cde068e919 Mon Sep 17 00:00:00 2001 From: Corson-Dosch <hcorson-dosch@usgs.gov> Date: Tue, 3 Sep 2024 23:11:12 -0500 Subject: [PATCH 15/25] get family labels working, adjust chart appearance --- src/components/FishAsFoodLinkChartViz.vue | 90 +++++++++++++++++------ 1 file changed, 68 insertions(+), 22 deletions(-) diff --git a/src/components/FishAsFoodLinkChartViz.vue b/src/components/FishAsFoodLinkChartViz.vue index cc51207..247eff4 100644 --- a/src/components/FishAsFoodLinkChartViz.vue +++ b/src/components/FishAsFoodLinkChartViz.vue @@ -65,6 +65,7 @@ let yAxis; let widthScale; let colorScale; + const transitionLength = 1500; // Create a reactive object to track expanded families const expandedFamilies = reactive({}); @@ -92,8 +93,8 @@ width: chart.value.offsetWidth, height: 1800, margin: 20, - marginBottom: 80, - marginLeft: 200}); + marginBottom: 95, + marginLeft: 350}); drawChart(data.value, scalePercent.value); } else { @@ -360,8 +361,16 @@ .nice() } + const xTickFormat = scalePercent ? d3.format("+.0%") : d3.format(".2f"); xAxisBottom.transition(getUpdateTransition()) - .call(d3.axisBottom(xScale).tickSize(0).tickPadding(10)); + .call(d3.axisBottom(xScale).tickSize(5).tickPadding(10).tickFormat(xTickFormat)) + .on("start", function(){ + xAxisBottom.select(".domain").remove() + // xAxisBottom.selectAll("text") + // .style("opacity", 0) + }) + // .selectAll("text") + // .style("opacity", 1); xAxisBottom .selectAll("text") @@ -369,24 +378,25 @@ const xAxisBottomLabelYPosition = xAxisBottom.select("text").attr('y') const xAxisBottomLabelDy = xAxisBottom.select("text").attr('dy') + const labelOffsetFactor = 5 xAxisBottom.append("text") .attr("class", "x-axis axis-title") .attr("x", chartDimensions.boundedWidth / 2) - .attr("y", xAxisBottomLabelYPosition * 4) + .attr("y", xAxisBottomLabelYPosition * labelOffsetFactor / 1.5) .attr("dy", xAxisBottomLabelDy) .style("text-anchor", "middle") .text(scalePercent ? 'Percent change in harvest-weighted climate vulnerability, 2030-2075' : 'Change in harvest-weighted climate vulnerability, 2030-2075') xAxisBottom.append("text") .attr("class", "x-axis axis-subtitle") .attr("x", 0) - .attr("y", xAxisBottomLabelYPosition * 5.5) + .attr("y", xAxisBottomLabelYPosition * labelOffsetFactor) .attr("dy", xAxisBottomLabelDy) .style("text-anchor", "start") .text('Less vulnerable') xAxisBottom.append("text") .attr("class", "x-axis axis-subtitle") .attr("x", chartDimensions.boundedWidth) - .attr("y", xAxisBottomLabelYPosition * 5.5) + .attr("y", xAxisBottomLabelYPosition * labelOffsetFactor) .attr("dy", xAxisBottomLabelDy) .style("text-anchor", "end") .text('More vulnerable') @@ -409,7 +419,7 @@ // Set range and domain for y scale yScale - .range([chartDimensions.boundedHeight, 0]) + .range([0, chartDimensions.boundedHeight]) .domain(yDomain) //yScale // .domain(d3.union(data.map(yAccessor))) //.sort(d3.ascending))) @@ -419,23 +429,55 @@ .transition(getUpdateTransition()) .attr("transform", `translate(0,${chartDimensions.boundedHeight})`) + + const xBuffer = 10; yAxis .transition(getUpdateTransition()) - .call(d3.axisLeft(yScale).tickSize(2)) - + .call(d3.axisLeft(yScale).tickSize(- chartDimensions.boundedWidth)) + .on("start", function(){ + yAxis.select(".domain").remove(); + yAxis + .selectAll("text") + .attr("x", -xBuffer) + }) + // repeat text position b/c of weird quirk + .selectAll("text") + .attr("x", -xBuffer) + yAxis .selectAll("text") - .attr("class", d => expandedFamilies[d] === false ? "axis-text family" : "axis-text species") + .attr("class", d => { + const current_data = data.filter(item => item.species === d)[0] + const current_family = current_data ? current_data.family : d + return expandedFamilies[d] === false ? "axis-text family " + current_family : "axis-text species family" + current_family + }) + + // Add family labels for expanded families + families.value.forEach(family => { + if (expandedFamilies[family]) { + setTimeout(() => { + const firstLabel = yAxis.select('.family' + family); + const firstLabelWidth = firstLabel.node().getComputedTextLength() + const firstLabelData = data.filter(d => d.family === family)[0] + const firstLabelYPosition = yScale(yAccessor(firstLabelData)) + const yAxisLabelDy = yAxis.select("text").attr('dy') + const yAxisLabelXPosition = yAxis.select("text").attr('x') + const yAxisLabelDx = yAxis.select("text").attr('dx') + yAxis.append("text") + .attr("class", "y-axis axis-title familyTitle" + family) + .attr("y", firstLabelYPosition + yScale.bandwidth() / 2) + .attr("x", yAxisLabelXPosition - firstLabelWidth - xBuffer) + .attr("dy", yAxisLabelDy) + .attr("dx", yAxisLabelDx) + .style("text-anchor", "end") + .text(family) + .style("opacity", 0) + .transition(getUpdateTransition()) + .style("opacity", 1) + }, transitionLength * 1.1); // give the chart a little extra time to render + } + }); - // const yAxisLabelXPosition = yAxis.select("text").attr('x') - // const yAxisLabelDx = yAxis.select("text").attr('dx') - // yAxis.append("text") - // .attr("class", "y-axis axis-title") - // .attr("y", 0) - // .attr("x", yAxisLabelXPosition) - // .attr("dx", yAxisLabelDx) - // .style("text-anchor", "end") - // .text('Species') // set up width scale const radiusPosition0 = 1 @@ -753,12 +795,12 @@ function getUpdateTransition () { return d3.transition() - .duration(1500) + .duration(transitionLength) .ease(d3.easeCubicInOut) } function getExitTransition() { return d3.transition() - .duration(1500) + .duration(transitionLength) .ease(d3.easeCubicInOut) } </script> @@ -768,7 +810,6 @@ font-size: 1.8rem; font-family: var(--default-font); user-select: none; - } .species { font-style: italic; @@ -791,4 +832,9 @@ fill: var(--color-text); user-select: none; } + .tick line { + stroke: #0f0f0f; + stroke-width: 0.5px; + stroke-dasharray: 1, 2; + } </style> \ No newline at end of file -- GitLab From 8faa2b5d7c2f953919957f87c69ebdd242b23b66 Mon Sep 17 00:00:00 2001 From: Corson-Dosch <hcorson-dosch@usgs.gov> Date: Wed, 4 Sep 2024 09:14:07 -0500 Subject: [PATCH 16/25] add references --- src/assets/text/references.js | 54 +++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/assets/text/references.js b/src/assets/text/references.js index 3818f74..788ce8d 100644 --- a/src/assets/text/references.js +++ b/src/assets/text/references.js @@ -167,6 +167,60 @@ export default { doi: "https://doi.org/10.1038/s43016-024-00961-8" }, ], + FishAsFoodLinkChart: [ + { + reference: "Reference 1.", + num: "1", + authors: "Lynch, A.J., Embke, H.S., Nyboer, E.A., Wood, L.E., Thorpe, A., Phang, S.C., Viana, D.F., & Golden, C.D.", + year: "2023", + title: "Global dataset of nutritional value, economic value, and climate vulnerability for species-specific recreational fisheries harvest for consumption", + journal: false, + journal_name: "", + journal_issue: "", + data_release: true, + link: "https://www.sciencebase.gov/catalog/item/644ae0e0d34e45f6ddccf773", + doi: "https://doi.org/10.5066/P9WO91SZ" + }, + { + reference: "Reference 2.", + num: "2", + authors: "Lynch, A.J., Embke, H.S., Nyboer, E.A., Wood, L.E., Thorpe, A., Phang, S.C., Viana, D.F., Golden, C.D., Milardi, M., Arlinghaus, R., Baigun, C., Beard, T.D., Cooke, S.J., Cowx, I.G., Koehn, J.D., Lyach, R., Potts, W., Robertson, A.M., Schmidhuber, J., & Weyl, O.L.F.", + year: "2024", + title: "Inland recreational fisheries contribute nutritional benefits and economic value but are vulnerable to climate change", + link: "https://www.nature.com/articles/s43016-024-00961-8", + journal: true, + journal_name: "Nature Food", + journal_issue: "5", + data_release: false, + doi: "https://doi.org/10.1038/s43016-024-00961-8" + }, + { + reference: "Reference 3.", + num: "3", + authors: "Nyboer, E. A., Embke, H. S., Robertson, A. M., Arlinghaus, R., Bower, S., Baigun, C., Beard, D., Cooke, S. J., Cowx, I. G., Koehn, J. D., Lyach, R., Milardi, M., Potts, W., & Lynch, A. J.", + year: "2021", + title: "Data for: Nyboer et al. 2021 Global assessment of marine and freshwater recreational fish reveals mismatch in climate change vulnerability and conservation effort", + link: "https://osf.io/keajr/", + journal: false, + journal_name: "", + journal_issue: "", + data_release: false, + doi: "https://doi.org/10.17605/osf.io/keajr" + }, + { + reference: "Reference 4.", + num: "4", + authors: "Nyboer, E.A., Lin, H., Bennett, J.R., Gabriel, J, Twardek, W., Chhor, A.D., Daly, L., Dolson, S., Guitard, E., Holder, P., Mozzon, C.M., Trahan, A., Zimmermann, D., Kesner-Reyes, K., Garilao, C., Kaschner, K., & Cooke, S.J.", + year: "2021", + title: "Global assessment of marine and freshwater recreational fish reveals mismatch in climate change vulnerability and conservation effort", + link: "https://onlinelibrary.wiley.com/doi/10.1111/gcb.15768?af=R", + journal: true, + journal_name: "Global Change Biology", + journal_issue: "527, 19", + data_release: false, + doi: "https://doi.org/10.1111/gcb.15768" + } + ], fishasfood: [ { reference: "Reference 1.", -- GitLab From dedfeb4ef6f2a94c88e0441ccb183c66eee6d35e Mon Sep 17 00:00:00 2001 From: Corson-Dosch <hcorson-dosch@usgs.gov> Date: Wed, 4 Sep 2024 09:14:20 -0500 Subject: [PATCH 17/25] add authors --- src/assets/text/authors.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/assets/text/authors.js b/src/assets/text/authors.js index fc83e21..e7796c6 100644 --- a/src/assets/text/authors.js +++ b/src/assets/text/authors.js @@ -208,4 +208,29 @@ export default { } ] }, + FishAsFoodLinkChart: { + 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 developer', + contribution: 'built this data visualization, and also co-led the design and development of the website' + }, + ], + additionalAuthors: [ + { + firstName: 'Maggie', + lastName: 'Jaenicke', + fullName: 'Maggie Jaenicke', + initials: 'MJ', + profile_link: 'https://www.usgs.gov/staff-profiles/margaret-maggie-jaenicke', + role: 'lead developer', + contribution: 'co-led the design and development of the website' + } + ] + }, }; \ No newline at end of file -- GitLab From d01dd811c0938afeacef0f55ced83408adb991e5 Mon Sep 17 00:00:00 2001 From: Corson-Dosch <hcorson-dosch@usgs.gov> Date: Wed, 4 Sep 2024 09:36:39 -0500 Subject: [PATCH 18/25] add placeholder color key --- src/assets/text/text.js | 2 +- src/components/FishAsFoodLinkChartViz.vue | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/assets/text/text.js b/src/assets/text/text.js index 42ae131..71bd4b4 100644 --- a/src/assets/text/text.js +++ b/src/assets/text/text.js @@ -96,7 +96,7 @@ export default { paragraph2: "The total economic value for each species in each country is calculated by multiplying the total kilograms of bimoass harvested for each species by the price per kilogram, in U.S. dollars. Species- and country-specific price data were collected from November 2021 to February 2022." }, FishAsFoodLinkChart: { - paragraph1: 'Climate vulnerability index.', + paragraph1: 'Climate vulnerability index. Climate vulnerability varies by family and species. <span class="warm"><b>warm</b></span>, <span class="cool"><b>cool</b></span>, or <span class="cold"><b>cold</b></span> thermal guilds.', explainerPart1: 'Showing the ', explainerPart2: 'from 2030 to 2075 in harvest-weighted climate vulnerability under representative concentration pathway 4.5.', prompt1: 'Click on the chart to show or hide data for <i>species</i> within each <b>family</b>' diff --git a/src/components/FishAsFoodLinkChartViz.vue b/src/components/FishAsFoodLinkChartViz.vue index 247eff4..31aca6f 100644 --- a/src/components/FishAsFoodLinkChartViz.vue +++ b/src/components/FishAsFoodLinkChartViz.vue @@ -805,6 +805,18 @@ } </script> +<style> + .warm { + color: #DB2500; + } + .cool { + color: #1474C2; + } + .cold { + color: #36648B; + } +</style> + <style lang="scss"> .axis-text { font-size: 1.8rem; -- GitLab From 93650c26481ba3a5af413bd64d3a19a47e75db74 Mon Sep 17 00:00:00 2001 From: Corson-Dosch <hcorson-dosch@usgs.gov> Date: Wed, 4 Sep 2024 13:02:11 -0500 Subject: [PATCH 19/25] add legend svg --- .../svgs/climate-vulnerability-legend.svg | 75 +++++++++++++++++++ src/components/FishAsFoodLinkChartViz.vue | 2 + 2 files changed, 77 insertions(+) create mode 100644 src/assets/svgs/climate-vulnerability-legend.svg diff --git a/src/assets/svgs/climate-vulnerability-legend.svg b/src/assets/svgs/climate-vulnerability-legend.svg new file mode 100644 index 0000000..2f93da6 --- /dev/null +++ b/src/assets/svgs/climate-vulnerability-legend.svg @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg id="chart-svg" xmlns="http://www.w3.org/2000/svg" width="350" height="100" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 350 100"> + <defs> + <style> + .cls-1 { + fill: url(#linear-gradient-2); + } + + .cls-2 { + fill: #231f20; + font-family: SourceSansPro-Bold, 'Source Sans Pro'; + font-size: 12px; + font-weight: 700; + } + + .cls-3, .cls-4 { + stroke-width: .5px; + } + + .cls-3, .cls-4, .cls-5, .cls-6 { + fill: #fff; + stroke-miterlimit: 10; + } + + .cls-3, .cls-6 { + stroke: #f37359; + } + + .cls-4, .cls-5 { + stroke: #66a9dd; + } + + .cls-5, .cls-6 { + stroke-width: 4px; + } + + .cls-7 { + fill: url(#linear-gradient); + } + </style> + <linearGradient id="linear-gradient" x1="152.4" y1="217.7" x2="162.2" y2="217.7" gradientTransform="translate(-4043 499) scale(26.7 -1.9)" gradientUnits="userSpaceOnUse"> + <stop offset=".4" stop-color="#66a9dd"/> + <stop offset="1" stop-color="#66a9dd" stop-opacity="0"/> + </linearGradient> + <linearGradient id="linear-gradient-2" x1="172.4" y1="237.6" x2="162.6" y2="237.6" gradientTransform="translate(-183.8 496) scale(2.9 -1.9)" gradientUnits="userSpaceOnUse"> + <stop offset=".4" stop-color="#f37359"/> + <stop offset="1" stop-color="#f37359" stop-opacity="0"/> + </linearGradient> + </defs> + <g id="legend-visuals"> + <g id="area-group-Percidae"> + <path id="area-Percidae" class="cls-7" d="M292.2,78.6L31.1,86.9v-18.9l261.1,8.3v2.4Z"/> + </g> + <g id="area-group-Pimelodidae"> + <path id="area-Pimelodidae" class="cls-1" d="M292.2,37.1l28.6,8.3v-18.9l-28.6,8.3v2.4Z"/> + </g> + <g id="point-2030-group-Percidae"> + <circle id="point-2030-Percidae_x5F_Zander_x5F__x28_pikeperch_x29_" class="cls-4" cx="292.2" cy="77.4" r="1.2"/> + </g> + <g id="point-2030-group-Pimelodidae"> + <circle id="point-2030-Pimelodidae" class="cls-3" cx="292.2" cy="35.9" r="1.2"/> + </g> + <g id="point-2030-group-Percidae-2" data-name="point-2030-group-Percidae"> + <circle id="point-2075-Percidae_x5F_Zander_x5F__x28_pikeperch_x29_" class="cls-5" cx="31.1" cy="77.4" r="7.4"/> + </g> + <g id="point-2030-group-Pimelodidae-2" data-name="point-2030-group-Pimelodidae"> + <circle id="point-2075-Pimelodidae" class="cls-6" cx="320.7" cy="35.9" r="7.4"/> + </g> + </g> + <g id="legend-labels"> + <text class="cls-2" transform="translate(279.4 60.5)"><tspan x="0" y="0">2030</tspan></text> + <text class="cls-2" transform="translate(18.3 60.5)"><tspan x="0" y="0">2075</tspan></text> + <text class="cls-2" transform="translate(308 19.3)"><tspan x="0" y="0">2075</tspan></text> + </g> +</svg> \ No newline at end of file diff --git a/src/components/FishAsFoodLinkChartViz.vue b/src/components/FishAsFoodLinkChartViz.vue index 31aca6f..515aae8 100644 --- a/src/components/FishAsFoodLinkChartViz.vue +++ b/src/components/FishAsFoodLinkChartViz.vue @@ -25,6 +25,7 @@ {{ text.explainerPart2 }} </p> <p v-html="text.prompt1" /> + <legendSVG /> </template> <template #figures> <div id="chart-container" class="maxWidth" ref="chart"></div> @@ -42,6 +43,7 @@ import { computed, onMounted, reactive, ref } from "vue"; //, reactive import * as d3 from 'd3'; import VizSection from '@/components/VizSection.vue'; + import legendSVG from '@/assets/svgs/climate-vulnerability-legend.svg' // define props defineProps({ -- GitLab From fac0fa0a238aebaef617240eefccc1f8b228b607 Mon Sep 17 00:00:00 2001 From: Corson-Dosch <hcorson-dosch@usgs.gov> Date: Wed, 4 Sep 2024 15:17:22 -0500 Subject: [PATCH 20/25] add data processing steps --- .gitignore | 1 + fishAsFood/_targets.R | 14 ++++ .../in/recfishfoods_families_thermal.xlsx | Bin 0 -> 34307 bytes fishAsFood/src/data_utils.R | 68 ++++++++++++++++-- 4 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 fishAsFood/in/recfishfoods_families_thermal.xlsx diff --git a/.gitignore b/.gitignore index f5faa2b..e90c346 100644 --- a/.gitignore +++ b/.gitignore @@ -52,6 +52,7 @@ examples/pydrograph/ *.html beaufortSea/images/* !*/images/in/* +!fishAsFood/in/recfishfoods_families_thermal.xlsx # allow empty files to be committed !*/in/.empty !*/out/.empty diff --git a/fishAsFood/_targets.R b/fishAsFood/_targets.R index 360eddd..d296258 100644 --- a/fishAsFood/_targets.R +++ b/fishAsFood/_targets.R @@ -26,6 +26,11 @@ p1 <- list( return(out_file) }, format = 'file' + ), + tar_target( + p2_metadata_xlsx, + file.path('in', 'recfishfoods_families_thermal.xlsx'), + format = 'file' ) ) @@ -42,6 +47,15 @@ p2 <- list( out_file = '../public/total_price.json' ), format = 'file' + ), + tar_target( + p2_climate_csv, + build_climate_csv( + data = p2_data, + metadata_file = p2_metadata_xlsx, + out_file = '../public/fish_as_food_climate.csv' + ), + format = 'file' ) ) diff --git a/fishAsFood/in/recfishfoods_families_thermal.xlsx b/fishAsFood/in/recfishfoods_families_thermal.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..9a84bc9f920218d9d7d79f18a10ec5374aa18e68 GIT binary patch literal 34307 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	+Kn^)xX0Zj!~gPQ9z2nS?7TJV{ni6t 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 a|1}~1Hg_VxjIUU`z#v<|6M_S;e)~VIDth<; literal 0 HcmV?d00001 diff --git a/fishAsFood/src/data_utils.R b/fishAsFood/src/data_utils.R index e134fbd..da0aefc 100644 --- a/fishAsFood/src/data_utils.R +++ b/fishAsFood/src/data_utils.R @@ -6,15 +6,19 @@ clean_input_data <- function(data_file) { data |> mutate( family = ifelse(family == 'Salmonindae', 'Salmonidae', family), - species_common = ifelse(species_common == 'Bey?ehir bleak', - 'BeyÅŸehir bleak', - species_common) + species_common = case_when( + species_common == 'Bey?ehir bleak' ~ 'BeyÅŸehir bleak', + species_taxa_sci == 'Brachyplatystoma rousseauxii' ~ 'Gilded catfish', + species_common == 'Speckled pavon, speckled peacock bass (percid)' ~ + 'Speckled pavon', + TRUE ~ species_common ) + ) } build_nested_json <- function(data, focal_columns, out_file) { data_list <- data |> - filter(!is.na(!!rlang::sym(focal_columns[['value']]))) |> + filter(!is.na(focal_columns[['value']])) |> group_by(name = family) |> group_modify(~ { species_list <- .x |> @@ -32,5 +36,61 @@ build_nested_json <- function(data, focal_columns, out_file) { json_data <- paste0('{\n "name": "fish", "children":', json_data, '\n}') write(json_data, file = out_file) + return(out_file) +} + +build_climate_csv <- function(data, metadata_file, out_file) { + # read in metadata + metadata <- read_xlsx(metadata_file) + + # filter out missing and uncertain data + data_subset <- data |> + filter(!is.na(species_common), !is.na(MCDM_VUL_2030_45)) |> + left_join(metadata) |> + filter(uncertainty_classification < 4) + + # get subset of better represented families with >2 species + focal_families <- data_subset |> + group_by(family, species_common) |> + count() |> + group_by(family) |> + count() |> + filter(n > 2) |> + pull(family) + + # filter to subset of families + data_subset <- data_subset |> + filter(family %in% focal_families) + + # aggregate to species level + data_species <- data_subset |> + group_by(family, species_common, thermal_guild, popular_y) |> + summarize(across(starts_with('MCDM'), + ~ first(.x)), + across(starts_with('weighted_MCDM'), + ~ mean(.x, na.rm = TRUE))) + + # sort aggregated data + subset_species <- pull(data_species, species_common) |> sort(decreasing = TRUE) + data_species <- mutate(data_species, species_common = + factor(species_common, levels = subset_species)) + + # aggregate to family level + data_family <- data_subset |> + group_by(family, thermal_guild, popular_y) |> + summarize(across(starts_with('MCDM'), + ~ first(.x)), + across(starts_with('weighted_MCDM'), + ~ mean(.x, na.rm = TRUE))) + + data_species |> + select(family, thermal_guild, species = species_common, + cvi_2030 = weighted_MCDM_VUL_2030_45, + cvi_2075 = weighted_MCDM_VUL_2075_45) |> + left_join(select(data_family, family, thermal_guild, + cvi_2030_family = weighted_MCDM_VUL_2030_45, + cvi_2075_family = weighted_MCDM_VUL_2075_45))|> + readr::write_csv(out_file) + return(out_file) } \ No newline at end of file -- GitLab From e97fc7ee5ebfd40dad2f0edd900d886503e2c106 Mon Sep 17 00:00:00 2001 From: Corson-Dosch <hcorson-dosch@usgs.gov> Date: Wed, 4 Sep 2024 15:18:01 -0500 Subject: [PATCH 21/25] clean up code and add comments --- src/assets/text/text.js | 2 +- src/components/FishAsFoodLinkChartViz.vue | 268 +++++++--------------- 2 files changed, 88 insertions(+), 182 deletions(-) diff --git a/src/assets/text/text.js b/src/assets/text/text.js index 71bd4b4..f81d30d 100644 --- a/src/assets/text/text.js +++ b/src/assets/text/text.js @@ -96,7 +96,7 @@ export default { paragraph2: "The total economic value for each species in each country is calculated by multiplying the total kilograms of bimoass harvested for each species by the price per kilogram, in U.S. dollars. Species- and country-specific price data were collected from November 2021 to February 2022." }, FishAsFoodLinkChart: { - paragraph1: 'Climate vulnerability index. Climate vulnerability varies by family and species. <span class="warm"><b>warm</b></span>, <span class="cool"><b>cool</b></span>, or <span class="cold"><b>cold</b></span> thermal guilds.', + paragraph1: 'Climate vulnerability index. Climate vulnerability varies by family and species. <span class="warm-text"><b>warm</b></span>, <span class="cool-text"><b>cool</b></span>, or <span class="cold-text"><b>cold</b></span> thermal guilds.', explainerPart1: 'Showing the ', explainerPart2: 'from 2030 to 2075 in harvest-weighted climate vulnerability under representative concentration pathway 4.5.', prompt1: 'Click on the chart to show or hide data for <i>species</i> within each <b>family</b>' diff --git a/src/components/FishAsFoodLinkChartViz.vue b/src/components/FishAsFoodLinkChartViz.vue index 515aae8..f37a233 100644 --- a/src/components/FishAsFoodLinkChartViz.vue +++ b/src/components/FishAsFoodLinkChartViz.vue @@ -64,8 +64,10 @@ let xScale; let xAxisBottom; let yScale; + const rowHeight = 25; let yAxis; let widthScale; + const colors = {warm: '#FF7256', cool: '#5CACEE', cold: '#36648B'} let colorScale; const transitionLength = 1500; // Create a reactive object to track expanded families @@ -93,7 +95,6 @@ initChart({ width: chart.value.offsetWidth, - height: 1800, margin: 20, marginBottom: 95, marginLeft: 350}); @@ -139,7 +140,6 @@ function initChart({ width = 500, // outer width, in pixels - // height = width, // outer height, in pixels margin = 1, // default margins marginTop = margin, // top margin, in pixels marginBottom = margin, // left margin, in pixels @@ -149,7 +149,6 @@ // set up global chart dimensions chartDimensions = { width, - //height, margin: { top: marginTop, right: marginRight, @@ -158,12 +157,11 @@ } } chartDimensions.boundedWidth = chartDimensions.width - chartDimensions.margin.left - chartDimensions.margin.right - //chartDimensions.boundedHeight = chartDimensions.height - chartDimensions.margin.top - chartDimensions.margin.bottom // draw canvas for chart chartSVG = d3.select("#chart-container") .append("svg") - //.attr("viewBox", [0, 0, (chartDimensions.width), (chartDimensions.height)].join(' ')) + // viewbox set on draw, since height is dynamic .attr("width", "100%") .attr("height", "100%") .attr("id", "chart-svg") @@ -211,17 +209,19 @@ } function initXAxis() { // add group for x axis + // will translate in drawChart, since height dynamic xAxisBottom = chartBounds.append("g") .attr("id", "x-axis") .attr("class", "axis") - //.attr("transform", `translate(0,${chartDimensions.boundedHeight})`) .attr("aria-hidden", true) // hide from screen reader + // Not generating here, will generate in drawChart() // generate x axis // xAxisBottom // .call(d3.axisBottom(xScale).tickSize(0).tickPadding(10)) // .select(".domain").remove() // remove axis line + // Not adding x-axis title here // // add placeholder for x axis title (title text set in drawChart()) // xAxisBottom // .append("text") @@ -233,12 +233,10 @@ } function initYScale() { - // scale for y axis (domain set in `drawChart()`) + // scale for y axis (domain and range set in `drawChart()`) yScale = d3.scaleBand() // .range([chartDimensions.boundedHeight, 0]) .padding(0.1) - - } function initYAxis() { // add group for y axis @@ -247,11 +245,13 @@ .attr("class", "axis") .attr("aria-hidden", true) + // Not generating here, will generate in drawChart() // generate y axis // yAxis // .call(d3.axisLeft(yScale).tickSize(2)) // .select(".domain").remove() // remove axis line + // Not adding y-axis title // // add placeholder for y axis title (title text set in drawChart()) // yAxis // .append("text") @@ -264,7 +264,6 @@ } function initGradients() { - let colors = {warm: '#FF7256', cool: '#5CACEE', cold: '#36648B'} Object.keys(colors).forEach(color => { let lg_decreasing = chartSVG.append("defs").append("linearGradient") .attr("id", color + "_gradient_decreasing") @@ -309,33 +308,15 @@ function initColorScale(data) { colorScale = d3.scaleOrdinal() .domain(data) - .range(data.map(item => getColor(item))); - } - - function getColor(item) { - let itemColor; - switch(item) { - case 'warm': - itemColor = '#FF7256'; - break; - case 'cool': - itemColor = '#5CACEE'; - break; - case 'cold': - itemColor = '#36648B'; - break; - default: - itemColor = '#B3B3B3'; - } - return itemColor; + .range(data.map(item => colors[item])); } function drawChart(data, scalePercent) { - // accessor functions + /////////////////////////////////////////// + ///// SET UP ACCESSOR FUNCTIONS ///// + /////////////////////////////////////////// const yAccessor = d => expandedFamilies[d.family] ? d.species : d.family //d.species - // const yAccessor_family = d => d.family const xAccessor = d => d.cvi - // const xAccessor_family = d => d.cvi_family const x0Accessor = d => scalePercent ? 0 : d.cvi_2030 const x1Accessor = d => scalePercent ? (d.cvi_2075 - d.cvi_2030)/ d.cvi_2030 : d.cvi_2075 const x0Accessor_family = d => scalePercent ? 0 : d.cvi_2030_family @@ -344,12 +325,9 @@ const colorAccessor = d => d.thermal_guild const identifierAccessor = d => expandedFamilies[d.family] ? d.family + '_' + d.species.replace(/ /g,"_") : d.family; - // to get dynamic - // need key for data - // need enter update exit pattern with transitions - // need to transition chart height, yscale, yaxis - - + //////////////////////////////// + ///// SET UP X AXIS ///// + /////////////////////////////// // set domain for xScale if (scalePercent) { const maxVal = d3.max([Math.abs(d3.max(data, x1Accessor)), Math.abs(d3.min(data, x1Accessor))]) @@ -363,21 +341,22 @@ .nice() } + // set tick format for x-axis const xTickFormat = scalePercent ? d3.format("+.0%") : d3.format(".2f"); + + // generate x-axis xAxisBottom.transition(getUpdateTransition()) .call(d3.axisBottom(xScale).tickSize(5).tickPadding(10).tickFormat(xTickFormat)) .on("start", function(){ xAxisBottom.select(".domain").remove() - // xAxisBottom.selectAll("text") - // .style("opacity", 0) }) - // .selectAll("text") - // .style("opacity", 1); + // assign class to x-axis text for css styling xAxisBottom .selectAll("text") .attr("class", "axis-text") + // Add titles to x-Axis const xAxisBottomLabelYPosition = xAxisBottom.select("text").attr('y') const xAxisBottomLabelDy = xAxisBottom.select("text").attr('dy') const labelOffsetFactor = 5 @@ -403,35 +382,35 @@ .style("text-anchor", "end") .text('More vulnerable') - // Remove axix line and labels - // xAxisBottom.select(".domain").remove() - // xAxisBottom.call(d3.axisBottom(xScale).tickValues([])) - + ////////////////////////////////////////// + ///// SET UP Y AXIS AND HEIGHT ///// + ////////////////////////////////////////// // set domain for yScale let yDomain = [] families.value.map(family => expandedFamilies[family] ? yDomain.push(data.filter(d => d.family === family).map(d => d.species)) : yDomain.push(family)) yDomain = yDomain.flat() - const totalHeight = yDomain.length * 25; + // compute chart height + const totalHeight = yDomain.length * rowHeight; chartDimensions.boundedHeight = totalHeight chartDimensions.height = chartDimensions.boundedHeight + chartDimensions.margin.top + chartDimensions.margin.bottom + + // set viewbox for SVG chartSVG .transition(getUpdateTransition()) .attr("viewBox", [0, 0, (chartDimensions.width), (chartDimensions.height)].join(' ')) - // Set range and domain for y scale - yScale - .range([0, chartDimensions.boundedHeight]) - .domain(yDomain) - //yScale - // .domain(d3.union(data.map(yAccessor))) //.sort(d3.ascending))) - // set y position for xAxisBottom xAxisBottom .transition(getUpdateTransition()) .attr("transform", `translate(0,${chartDimensions.boundedHeight})`) - + // Set range and domain for y scale + yScale + .range([0, chartDimensions.boundedHeight]) + .domain(yDomain) + + // generate y axis const xBuffer = 10; yAxis .transition(getUpdateTransition()) @@ -446,6 +425,7 @@ .selectAll("text") .attr("x", -xBuffer) + // assign class to y-axis labels yAxis .selectAll("text") .attr("class", d => { @@ -454,62 +434,70 @@ return expandedFamilies[d] === false ? "axis-text family " + current_family : "axis-text species family" + current_family }) - // Add family labels for expanded families + // Add family labels for expanded families, waiting until chart is rendered to determine x placement + // start by making sure all existing labels are removed families.value.forEach(family => { if (expandedFamilies[family]) { setTimeout(() => { - const firstLabel = yAxis.select('.family' + family); - const firstLabelWidth = firstLabel.node().getComputedTextLength() - const firstLabelData = data.filter(d => d.family === family)[0] - const firstLabelYPosition = yScale(yAccessor(firstLabelData)) - const yAxisLabelDy = yAxis.select("text").attr('dy') - const yAxisLabelXPosition = yAxis.select("text").attr('x') - const yAxisLabelDx = yAxis.select("text").attr('dx') - yAxis.append("text") - .attr("class", "y-axis axis-title familyTitle" + family) - .attr("y", firstLabelYPosition + yScale.bandwidth() / 2) - .attr("x", yAxisLabelXPosition - firstLabelWidth - xBuffer) - .attr("dy", yAxisLabelDy) - .attr("dx", yAxisLabelDx) - .style("text-anchor", "end") - .text(family) - .style("opacity", 0) - .transition(getUpdateTransition()) - .style("opacity", 1) + // confirm family is still expanded + if (expandedFamilies[family]) { + const firstLabel = yAxis.select('.family' + family); + const firstLabelWidth = firstLabel.node().getComputedTextLength() + const firstLabelData = data.filter(d => d.family === family)[0] + const firstLabelYPosition = yScale(yAccessor(firstLabelData)) + const yAxisLabelDy = yAxis.select("text").attr('dy') + const yAxisLabelXPosition = yAxis.select("text").attr('x') + const yAxisLabelDx = yAxis.select("text").attr('dx') + yAxis.append("text") + .attr("class", "y-axis axis-title familyTitle" + family) + .attr("y", firstLabelYPosition + yScale.bandwidth() / 2) + .attr("x", yAxisLabelXPosition - firstLabelWidth - xBuffer) + .attr("dy", yAxisLabelDy) + .attr("dx", yAxisLabelDx) + .style("text-anchor", "end") + .text(family) + .style("opacity", 0) + .transition(getUpdateTransition()) + .style("opacity", 1) + } }, transitionLength * 1.1); // give the chart a little extra time to render } }); - - // set up width scale + //////////////////////////////////// + ///// SET UP WIDTH SCALE ///// + //////////////////////////////////// const radiusPosition0 = 1 const strokeRatio = 0.3 const strokeWidth1 = strokeRatio * yScale.bandwidth() / 2 const radiusPosition1 = (1 - strokeRatio) * yScale.bandwidth() / 2 initWidthScale(radiusPosition0, radiusPosition1) - // set up area function + ///////////////////////////////////// + ///// SET UP AREA FUNCTION ///// + ///////////////////////////////////// const area = d3.area() .x(d => xScale(xAccessor(d))) .y0(d => yScale(yAccessor(d)) + yScale.bandwidth() / 2 - widthScale(widthAccessor(d))) .y1(d => yScale(yAccessor(d)) + yScale.bandwidth() / 2+ widthScale(widthAccessor(d))); - // .x(d => expandedFamilies[d.family] ? xScale(xAccessor(d)) : xScale(xAccessor_family(d))) - // .y0(d => expandedFamilies[d.family] ? yScale(yAccessor(d)) - widthScale(widthAccessor(d)) : yScale(yAccessor_family(d)) - widthScale(widthAccessor(d))) - // .y1(d => expandedFamilies[d.family] ? yScale(yAccessor(d)) + widthScale(widthAccessor(d)) : yScale(yAccessor_family(d)) + widthScale(widthAccessor(d))); - // set up color scale + /////////////////////////////////// + ///// SET UP COLOR SCALE ///// + /////////////////////////////////// const colorCategories = Array.from(new Set(data.map(colorAccessor))) initColorScale(colorCategories) - // set up area data - const areaCategories = Array.from(new Set(data.map(d => d.species))) //Array.from(new Set(data.map(yAccessor))) + /////////////////////////////////////// + ///// SET UP DATA FOR AREAS ///// + /////////////////////////////////////// + const areaCategories = Array.from(new Set(data.map(d => d.species))) const areaData = areaCategories.map(areaCategory => { - const species = areaCategory //expandedFamilies[areaCategory] === false ? null : areaCategory; - const family = data.filter(d => d.species === species)[0].family //expandedFamilies[areaCategory] === false ? areaCategory : data.filter(d => d.species === species)[0].family; - const cvi_2030 = expandedFamilies[family] ? x0Accessor(data.filter(d => d.species === species)[0]) : x0Accessor_family(data.filter(d => d.family === family)[0]) //species ? x0Accessor(data.filter(d => d.species === species)[0]) : x0Accessor_family(data.filter(d => d.family === family)[0]) + const species = areaCategory + const family = data.filter(d => d.species === species)[0].family + const cvi_2030 = expandedFamilies[family] ? x0Accessor(data.filter(d => d.species === species)[0]) : x0Accessor_family(data.filter(d => d.family === family)[0]) const cvi_2075 = expandedFamilies[family] ? x1Accessor(data.filter(d => d.species === species)[0]) : x1Accessor_family(data.filter(d => d.family === family)[0]) const cvi_decreasing = cvi_2030 > cvi_2075 - const thermal_guild = colorAccessor(data.filter(d => d.species === species)[0]) //species ? colorAccessor(data.filter(d => d.species === species)[0]) : colorAccessor(data.filter(d => d.family === family)[0]) + const thermal_guild = colorAccessor(data.filter(d => d.species === species)[0]) return [ { @@ -517,7 +505,6 @@ family: family, cvi: cvi_2030, cvi_decreasing: cvi_decreasing, - // cvi_family: x0Accessor_family(data.filter(d => d.species === areaCategory)[0]), position: 0, thermal_guild: thermal_guild }, @@ -526,13 +513,15 @@ family: family, cvi: cvi_2075, cvi_decreasing: cvi_decreasing, - // cvi_family: x1Accessor_family(data.filter(d => d.species === areaCategory)[0]), position: 1, thermal_guild: thermal_guild } ] }) - + + //////////////////////////////////// + ///// ADD CHART ELEMENTS ///// + //////////////////////////////////// // draw chart // Enter-update-exit pattern for areas let areaGroups = chartBounds.selectAll(".areas") @@ -570,33 +559,6 @@ .attr('d', d => area(d)) .attr('fill', d => d[0].cvi_decreasing ? `url(#${d[0].thermal_guild}_gradient_decreasing)`: `url(#${d[0].thermal_guild}_gradient_increasing)`)//d => colorScale(colorAccessor(d[0]))) .style("opacity", 1) - - // chartBounds.append("g") - // .attr("id", "areas") - // .selectAll('.area') - // .data(areaData, d => d[0].species) - // .enter() - // .append('path') - // .attr("id", d => 'area-' + identifierAccessor(d[0])) - // .attr('class', "area") - // .attr('d', d => area(d)) - // .attr('fill', d => d[0].cvi_decreasing ? `url(#${d[0].thermal_guild}_gradient_decreasing)`: `url(#${d[0].thermal_guild}_gradient_increasing)`)//d => colorScale(colorAccessor(d[0]))) - // .style("opacity", 1) - // .on("click", function(event, d) { - // const clickedFamily = d[0].family - // expandedFamilies[clickedFamily] = !expandedFamilies[clickedFamily] - // let yDomain = [] - // families.value.map(family => expandedFamilies[family] ? yDomain.push(data.filter(d => d.family === family).map(d => d.species)) : yDomain.push(family)) - // yDomain = yDomain.flat() - // const yBandwidth = yScale.bandwidth() - // chartDimensions.boundedHeight = yBandwidth * yDomain.length; - // chartDimensions.height = chartDimensions.boundedHeight + chartDimensions.margin.top + chartDimensions.margin.bottom; - // chartSVG.attr("viewBox", [0, 0, (chartDimensions.width), (chartDimensions.height)].join(' ')) - // yScale.range([chartDimensions.boundedHeight * 2, 0]) - // yAxis - // .call(d3.axisLeft(yScale).tickSize(2)) - // drawChart(data, scalePercent.value) - // }); // Enter-Update-Exit pattern for 2030 points let pointGroups2030 = chartBounds.selectAll('.points_2030') @@ -641,21 +603,6 @@ .style("stroke", d => colorScale(colorAccessor(d))) .style("fill", "white") - // chartBounds.append("g") - // .attr("id", "points-2030") - // .attr("class", "points points_2030") - // .selectAll('points') - // .data(data) - // .enter() - // .append("circle") - // .attr("id", d => 'point-2030-' + identifierAccessor(d)) - // .attr("class", "point") - // .attr("cx", d => xScale(x0Accessor(d))) - // .attr("cy", d => yScale(yAccessor(d)) + yScale.bandwidth() / 2) - // .attr("r", radiusPosition0) - // .style("stroke", d => colorScale(colorAccessor(d))) - // .style("fill", "white") - // Enter-Update-Exit pattern for 2075 points let pointGroups2075 = chartBounds.selectAll('.points_2075') .selectAll(".point_2075") @@ -701,39 +648,6 @@ .style("stroke-width", strokeWidth1) .style("fill", "white") - // chartBounds.append("g") - // .attr("id", "points-2075") - // .attr("class", "points points_2075") - // .selectAll('points') - // .data(data) - // .enter() - // .append("circle") - // .attr("id", d => 'point-2075-' + identifierAccessor(d)) - // .attr("class", "point") - // .attr("cx", d => xScale(x1Accessor(d))) - // .attr("cy", d => yScale(yAccessor(d)) + yScale.bandwidth() / 2) - // .attr("r", radiusPosition1 - strokeWidth1 / 2) - // .style("stroke", d => colorScale(colorAccessor(d))) - // .style("stroke-width", strokeWidth1) - // .style("fill", "white") - - // chartBounds.append("g") - // .attr("id", "eyes-2075") - // .attr("class", "eyes eyes_2075") - // .selectAll('eyes') - // .data(data) - // .enter() - // .append("circle") - // .attr("id", d => 'point-2075-' + identifierAccessor(d)) - // .attr("class", "point") - // .attr("cx", d => { - // return x1Accessor(d) > x0Accessor(d) ? (xScale(x1Accessor(d)) + radiusPosition1 / 4) : (xScale(x1Accessor(d)) - radiusPosition1 / 4) - // }) - // .attr("cy", d => yScale(yAccessor(d)) + radiusPosition1 / 4) - // .attr("r", radiusPosition1 / 2) - // .style("stroke", 'none') - // .style("fill", "black") - // Enter-Update-Exit pattern for overlay rectangles let rectGroups = chartBounds.selectAll('.rects') .selectAll(".rect") @@ -751,7 +665,7 @@ .attr("class", d => "rect " + d.species) .attr("id", d => 'rect-group-' + identifierAccessor(d)) - // append points + // append rectangles newRectGroups .append("rect") .attr("id", d => 'rect-' + identifierAccessor(d)) @@ -762,12 +676,12 @@ .attr("width", chartDimensions.width) .style("fill", "transparent") - // update pointGroups2075 to include new points + // update rectGroups to include new rectangles rectGroups = newRectGroups.merge(rectGroups) const allRects = rectGroups.select("rect") - // Update points based on data values + // Update rectangles based on data values allRects.transition(getUpdateTransition()) .attr("id", d => 'rect-' + identifierAccessor(d)) .attr("class", "rect") @@ -781,16 +695,8 @@ .on("click", function(event, d) { const clickedFamily = d.family expandedFamilies[clickedFamily] = !expandedFamilies[clickedFamily] - // let yDomain = [] - // families.value.map(family => expandedFamilies[family] ? yDomain.push(data.filter(d => d.family === family).map(d => d.species)) : yDomain.push(family)) - // yDomain = yDomain.flat() - // const yBandwidth = yScale.bandwidth() - // chartDimensions.boundedHeight = yBandwidth * yDomain.length; - // chartDimensions.height = chartDimensions.boundedHeight + chartDimensions.margin.top + chartDimensions.margin.bottom; - // chartSVG.attr("viewBox", [0, 0, (chartDimensions.width), (chartDimensions.height)].join(' ')) - // yScale.range([chartDimensions.boundedHeight * 2, 0]) - // yAxis - // .call(d3.axisLeft(yScale).tickSize(2)) + + // redraw chart drawChart(data, scalePercent) }); } @@ -808,13 +714,13 @@ </script> <style> - .warm { + .warm-text { color: #DB2500; } - .cool { + .cool-text { color: #1474C2; } - .cold { + .cold-text { color: #36648B; } </style> -- GitLab From 99490bdd6364927aaa022cc47f8b58538757ce2e Mon Sep 17 00:00:00 2001 From: Corson-Dosch <hcorson-dosch@usgs.gov> Date: Wed, 4 Sep 2024 15:20:32 -0500 Subject: [PATCH 22/25] have chart collapsed by default --- src/components/FishAsFoodLinkChartViz.vue | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/FishAsFoodLinkChartViz.vue b/src/components/FishAsFoodLinkChartViz.vue index f37a233..ff6d3bd 100644 --- a/src/components/FishAsFoodLinkChartViz.vue +++ b/src/components/FishAsFoodLinkChartViz.vue @@ -90,8 +90,6 @@ families.value.forEach(family => { expandedFamilies[family] = false; }); - // expandedFamilies["Cyprinidae"] = !expandedFamilies["Cyprinidae"] - expandedFamilies["Salmonidae"] = !expandedFamilies["Salmonidae"] initChart({ width: chart.value.offsetWidth, -- GitLab From b5373de2468c31a87bcd3d220238644761454ee0 Mon Sep 17 00:00:00 2001 From: Corson-Dosch <hcorson-dosch@usgs.gov> Date: Wed, 4 Sep 2024 15:35:07 -0500 Subject: [PATCH 23/25] only transition x axis if not initial load --- src/components/FishAsFoodLinkChartViz.vue | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/components/FishAsFoodLinkChartViz.vue b/src/components/FishAsFoodLinkChartViz.vue index ff6d3bd..3b7f83d 100644 --- a/src/components/FishAsFoodLinkChartViz.vue +++ b/src/components/FishAsFoodLinkChartViz.vue @@ -52,6 +52,7 @@ // global variables const publicPath = import.meta.env.BASE_URL; + const initialLoad = ref(); const dataFile = 'fish_as_food_climate.csv' //'fish_as_food_climate_test.csv' const data = ref(); const families = ref(); @@ -78,12 +79,13 @@ return scalePercent.value ? 'percent change' : 'change' }); - // Behavior on mounted (functions called here) // Load data and then make chart onMounted(async () => { try { await loadDatasets(); + initialLoad.value = true; + if (data.value.length > 0) { families.value = Array.from(new Set(data.value.map(d => d.family))); // Initialize the expandedFamilies object @@ -98,6 +100,7 @@ marginLeft: 350}); drawChart(data.value, scalePercent.value); + initialLoad.value = false; } else { console.error('Error loading data'); } @@ -398,10 +401,15 @@ .transition(getUpdateTransition()) .attr("viewBox", [0, 0, (chartDimensions.width), (chartDimensions.height)].join(' ')) - // set y position for xAxisBottom - xAxisBottom - .transition(getUpdateTransition()) - .attr("transform", `translate(0,${chartDimensions.boundedHeight})`) + // set y position for xAxisBottom, transitioning it if NOT initial load + if (initialLoad.value) { + xAxisBottom + .attr("transform", `translate(0,${chartDimensions.boundedHeight})`) + } else { + xAxisBottom + .transition(getUpdateTransition()) + .attr("transform", `translate(0,${chartDimensions.boundedHeight})`) + } // Set range and domain for y scale yScale -- GitLab From 70b335023a30e54eb6e6c458f63040f6c1496855 Mon Sep 17 00:00:00 2001 From: Corson-Dosch <hcorson-dosch@usgs.gov> Date: Wed, 4 Sep 2024 15:41:01 -0500 Subject: [PATCH 24/25] bump up margin --- src/components/FishAsFoodLinkChartViz.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/FishAsFoodLinkChartViz.vue b/src/components/FishAsFoodLinkChartViz.vue index 3b7f83d..11c0306 100644 --- a/src/components/FishAsFoodLinkChartViz.vue +++ b/src/components/FishAsFoodLinkChartViz.vue @@ -95,7 +95,7 @@ initChart({ width: chart.value.offsetWidth, - margin: 20, + margin: 25, marginBottom: 95, marginLeft: 350}); -- GitLab From 489412ac42d81a6567b831482e370cd415118755 Mon Sep 17 00:00:00 2001 From: Corson-Dosch <hcorson-dosch@usgs.gov> Date: Wed, 4 Sep 2024 23:01:12 -0500 Subject: [PATCH 25/25] simplify author statement --- src/assets/text/authors.js | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/assets/text/authors.js b/src/assets/text/authors.js index 5b66d38..253beb0 100644 --- a/src/assets/text/authors.js +++ b/src/assets/text/authors.js @@ -177,19 +177,10 @@ export default { profile_link: 'https://www.usgs.gov/staff-profiles/hayley-corson-dosch', role: 'lead developer', - contribution: 'built this data visualization, and also co-led the design and development of the website' + contribution: 'built this data visualization' }, ], additionalAuthors: [ - { - firstName: 'Maggie', - lastName: 'Jaenicke', - fullName: 'Maggie Jaenicke', - initials: 'MJ', - profile_link: 'https://www.usgs.gov/staff-profiles/margaret-maggie-jaenicke', - role: 'lead developer', - contribution: 'co-led the design and development of the website' - } ] }, }; \ No newline at end of file -- GitLab