Skip to content

Commit

Permalink
Merge pull request #37 from earthrise-media/non-gridded-dots
Browse files Browse the repository at this point in the history
Add cards and distribution plots
  • Loading branch information
Martin Bernard authored Nov 20, 2023
2 parents 19b776a + 13ed49e commit fb88444
Show file tree
Hide file tree
Showing 5 changed files with 241 additions and 29 deletions.
3 changes: 2 additions & 1 deletion vacs-map-app/src/assets/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
#app {
margin: 0 auto;

--black: #272727;
--white: #E1DCD5;
--dark-gray: #404040;
--white: #ffffff;

--page-horizontal-margin: 5rem;
}
84 changes: 84 additions & 0 deletions vacs-map-app/src/components/CardWrapper.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<template>
<div class="card-wrapper" @click="handleClick" :class="{ active: isActive }">
<slot></slot>

<div class="info">
<div class="title"> {{ title }} </div>
<p class="description"> {{ description }} </p>
</div>
</div>
</template>

<script setup>
import { toRefs} from 'vue';
const props = defineProps({
title: {
type: String,
default: '',
},
description: {
type: String,
default: '',
},
handleClick: {
type: Function,
default: () => {}
},
isActive: {
type: Boolean,
default: false
}
});
const {title, description, handleClick} = toRefs(props);
</script>

<style scoped>
.card-wrapper {
--title-height: 3.5rem;
position: relative;
overflow-y: hidden;
border: 1px solid transparent;
border-radius: 1rem;
cursor: pointer;
}
.active {
border-color: var(--white);
}
.info {
transition: all 0.5s ease;
position: absolute;
top: calc(100% - var(--title-height));
padding: 0 1rem;
display: flex;
flex-direction: column;
width: 100%;
background: var(--white);
color: var(--black);
}
.title {
height: var(--title-height);
font-size: 1.5rem;
text-transform: capitalize;
display: flex;
align-items: center;
overflow-x: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.info:hover {
transform: translateY(calc((100% - var(--title-height))*-1));
}
</style>
28 changes: 19 additions & 9 deletions vacs-map-app/src/components/CropCards.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
<template>
<div class="crop-cards">
<div class="crop-card" v-for="crop in filteredCrops" :key="crop">
<router-link :to="`/map-explore`" @click="selectedCrop = crop">
{{ crop }}
</router-link>
</div>
<CardWrapper
v-for="crop in filteredCrops"
:key="crop"
:title="crop"
:description="'description'"
:handle-click="() => navigate(crop)"
>
<div class="card-background"></div>
</CardWrapper>
</div>
</template>

Expand All @@ -14,6 +18,7 @@ import { computed } from 'vue';
import { useRouter } from 'vue-router';
import { storeToRefs } from 'pinia';
import { useFiltersStore } from '@/stores/filters';
import CardWrapper from './CardWrapper.vue';
const router = useRouter();
const filtersStore = useFiltersStore();
Expand All @@ -22,6 +27,11 @@ const { availableCrops, selectedCrop } = storeToRefs(filtersStore);
// TODO actually filter
const filteredCrops = computed(() => availableCrops.value);
const navigate = (crop) => {
selectedCrop.value = crop;
router.push('map-explore');
}
</script>

<style scoped>
Expand All @@ -31,9 +41,9 @@ const filteredCrops = computed(() => availableCrops.value);
gap: 1rem;
}
.crop-card {
background: white;
border-radius: 10px;
height: 10rem;
.card-background {
height: 15rem;
width: 100%;
background: var(--black);
}
</style>
121 changes: 121 additions & 0 deletions vacs-map-app/src/components/DistributionPlot.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<template>
<div class="svg-wrapper" ref="wrapperRef">
<svg>
<line
v-for="cell in gridCells"
:key="cell.id"
:x1="xScale(0)"
:x2="xScale(1)"
:y1="yScale(cell.val)"
:y2="yScale(cell.val)"
:stroke="getCellColor(cell.val)"
stroke-width="0.05"
stroke-opacity="1"
/>
</svg>
</div>
</template>

<script setup>
import * as d3 from 'd3';
import { useResizeObserver } from '@vueuse/core';
import { computed, toRefs, ref, onMounted } from 'vue';
import { useFiltersStore } from '@/stores/filters';
import { useCropYieldsStore } from '@/stores/cropYields';
import { storeToRefs } from 'pinia';
const props = defineProps({
scenario: {
type: String,
default: '',
},
});
const {scenario} = toRefs(props);
const cropYieldsStore = useCropYieldsStore();
const filtersStore = useFiltersStore();
const { selectedMetric, selectedCrop, availableModels, availableCrops } = storeToRefs(filtersStore);
const { data: cropYieldsData } = storeToRefs(cropYieldsStore);
const wrapperRef = ref(null);
const width = ref(0);
const height = ref(0);
useResizeObserver(wrapperRef, ([entry]) => {
width.value = entry.contentRect.width;
height.value = entry.contentRect.height;
})
const margin = computed(() => {
return height.value * .2;
})
const columnName = computed(() => {
return [selectedMetric.value, selectedCrop.value, scenario.value].join("_")
})
const metaExtent = computed(() => {
// want to get extent across all scenarios and included crops so that comparisons are more useful
const extents = []
availableModels.value.forEach(s => {
availableCrops.value.forEach(c => {
const column = [selectedMetric.value, c, s].join("_");
extents.push(cropYieldsStore.getExtent(column))
})
})
return [d3.min(extents.map(d => d[0])), d3.min(extents.map(d => d[1]))];
})
const gridCells = computed(() => {
if (!cropYieldsData.value) return;
return cropYieldsData.value.map((row) => {
return {
id: row.id,
val: row[columnName.value],
};
});
});
const yScale = computed(() => {
return d3.scaleLinear()
.domain(metaExtent.value)
.range([height.value - margin.value, margin.value])
// .clamp(true);
})
const xScale = computed(() => {
return d3.scaleLinear()
.domain([0,1])
.range([0, width.value])
})
const getCellColor = (value) => {
if (!value) return 'transparent';
const scale = d3.scaleLinear()
.domain([metaExtent.value[0], 0, metaExtent.value[1]])
.range(["#FF8A00", "#D4D5A5", "#1CAC50"])
.clamp(true);
return scale(value);
}
onMounted(() => {
width.value = wrapperRef.value.clientWidth;
height.value = wrapperRef.value.clientHeight;
});
</script>

<style scoped>
.svg-wrapper {
height: 100%;
width: 100%;
padding: 1rem;
}
svg {
width: 100%;
height: 100%;
}
</style>
34 changes: 15 additions & 19 deletions vacs-map-app/src/components/ExploreSidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,16 @@
</div>

<div class="scenarios">
<div v-for="scenario in futureScenarios" :key="scenario" class="scenario" :class="{selected: selectedModel === scenario}" @click="selectedModel = scenario">
<span>
{{ scenario }}
</span>
</div>
<CardWrapper
v-for="scenario in futureScenarios"
:key="scenario"
:title="scenario"
:description="'this is a longer description to test how a longer description looks'"
:is-active="selectedModel === scenario"
:handle-click="() => selectedModel = scenario"
>
<DistributionPlot :scenario="scenario" />
</CardWrapper>
</div>
</div>
</template>
Expand All @@ -30,6 +35,8 @@
import { computed, ref } from 'vue';
import { storeToRefs } from 'pinia';
import { useFiltersStore } from '@/stores/filters';
import DistributionPlot from './DistributionPlot.vue';
import CardWrapper from './CardWrapper.vue';
const filtersStore = useFiltersStore();
const {
Expand All @@ -47,9 +54,10 @@ const futureScenarios = computed(() => availableModels.value.filter(d => d.start
.sidebar {
display: flex;
flex-direction: column;
gap: 1rem;
justify-content: space-between;
margin-left: var(--page-horizontal-margin);
padding-right: 2rem;
padding-bottom: 2rem;
width: 400px;
}
Expand All @@ -62,7 +70,7 @@ const futureScenarios = computed(() => availableModels.value.filter(d => d.start
.crop-fingerprint {
width: 100%;
height: 40%;
border: 1px solid white;
border: 1px solid var(--white);
border-radius: 0.5rem;
}
Expand All @@ -74,16 +82,4 @@ const futureScenarios = computed(() => availableModels.value.filter(d => d.start
gap: 1rem;
}
.scenario {
width: 100%;
border: 1px solid gray;
border-radius: 0.5rem;
cursor: pointer;
}
.scenario.selected {
cursor: pointer;
border-color: white;
}
</style>

0 comments on commit fb88444

Please sign in to comment.