Stacked area chart
Этот коммит содержится в:
родитель
dfb3ca9b36
Коммит
568e040cd2
22
package-lock.json
сгенерированный
22
package-lock.json
сгенерированный
@ -17,6 +17,7 @@
|
||||
"d3-array": "^3.2.4",
|
||||
"d3-fetch": "^3.0.1",
|
||||
"d3-scale": "^4.0.2",
|
||||
"d3-shape": "^3.2.0",
|
||||
"d3-time-format": "^4.1.0",
|
||||
"eslint": "^9.0.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
@ -1289,6 +1290,15 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-path": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
|
||||
"integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-scale": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
|
||||
@ -1305,6 +1315,18 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-shape": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
|
||||
"integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"d3-path": "^3.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-time": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
"d3-array": "^3.2.4",
|
||||
"d3-fetch": "^3.0.1",
|
||||
"d3-scale": "^4.0.2",
|
||||
"d3-shape": "^3.2.0",
|
||||
"d3-time-format": "^4.1.0",
|
||||
"eslint": "^9.0.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
<script>
|
||||
import { scaleUtc, scalePoint, scaleOrdinal } from 'd3-scale';
|
||||
import { scaleUtc, scalePoint, scaleOrdinal, scaleLinear, scaleTime } from 'd3-scale';
|
||||
import { extent } from 'd3-array';
|
||||
import { utcFormat } from 'd3-time-format';
|
||||
import { area, stack, curveNatural } from 'd3-shape';
|
||||
import { max, union, index } from 'd3-array';
|
||||
import { fade } from 'svelte/transition';
|
||||
import Bubble from '$lib/components/Bubble.svelte';
|
||||
import Square from '$lib/components/Square.svelte';
|
||||
@ -9,6 +11,7 @@
|
||||
|
||||
export let cases;
|
||||
export let events;
|
||||
export let metrics;
|
||||
|
||||
const margins = {
|
||||
top: 24,
|
||||
@ -17,30 +20,12 @@
|
||||
left: 120
|
||||
};
|
||||
|
||||
const keyEvents = [
|
||||
{
|
||||
date: new Date('2024-03-21'),
|
||||
title: 'Key event number 1',
|
||||
description: 'A very interesting event that happened somewhere.'
|
||||
},
|
||||
{
|
||||
date: new Date('2024-04-25'),
|
||||
title: 'Key event number 2',
|
||||
description: 'A somewhat less interesting event, still interesting, though.'
|
||||
},
|
||||
{
|
||||
date: new Date('2024-08-01'),
|
||||
title: 'Key event number 3',
|
||||
description: 'Yet another key event.'
|
||||
}
|
||||
];
|
||||
|
||||
let width;
|
||||
let height = 280;
|
||||
|
||||
$: dateExtent = extent(cases.map((d) => new Date(d.attribution_date)));
|
||||
|
||||
$: xScale = scaleUtc(dateExtent, [0, width - margins.right - margins.left]);
|
||||
$: xScale = scaleTime(dateExtent, [0, width - margins.right - margins.left]);
|
||||
$: ticks = xScale.ticks(5);
|
||||
|
||||
const actorNations = ['Key event', 'China', 'Iran', 'North Korea', 'Russia'];
|
||||
@ -49,6 +34,29 @@
|
||||
let yScale = scalePoint(actorNations, [height - margins.bottom - margins.top, 0]).padding(1);
|
||||
let colorScale = scaleOrdinal(actorNations, colors);
|
||||
|
||||
$: stackedMetrics = stack()
|
||||
.keys(union(metrics.map((d) => d.country)))
|
||||
//.keys(["China", "Iran", "Russia"])
|
||||
.value(([, D], key) => D.get(key).posts)
|
||||
(index(metrics, d => d.date, d => d.country));
|
||||
$: console.log(stackedMetrics)
|
||||
|
||||
let stackMax = 0;
|
||||
$: if (stackedMetrics.length > 0) {
|
||||
stackMax = max(stackedMetrics[stackedMetrics.length - 1].map((d) => d[1]));
|
||||
}
|
||||
|
||||
$: yScaleStack = scaleLinear([0, stackMax], [800 - margins.bottom - margins.top, 0]);
|
||||
let areaGenerator
|
||||
$: if(xScale && yScaleStack) {
|
||||
areaGenerator = area()
|
||||
.x((d) => xScale(d.data[0]))
|
||||
.y0((d) => yScaleStack(d[0]))
|
||||
.y1((d) => yScaleStack(d[1]))
|
||||
.curve(curveNatural)
|
||||
}
|
||||
|
||||
// Tooltip
|
||||
let showTooltip = false;
|
||||
let tooltipContent;
|
||||
let tooltipX;
|
||||
@ -142,16 +150,28 @@
|
||||
stroke-width={2}
|
||||
ttContent={`<p style='font-weight; bold;'>${event.Title}</p>
|
||||
<p>${event.Description}</p>`}
|
||||
bind:tooltipContent
|
||||
bind:tooltipX
|
||||
bind:tooltipY
|
||||
bind:showTooltip
|
||||
bind:tooltipContent
|
||||
bind:tooltipX
|
||||
bind:tooltipY
|
||||
bind:showTooltip
|
||||
></Square>
|
||||
{/each}
|
||||
{/if}
|
||||
</g>
|
||||
{/if}
|
||||
</svg>
|
||||
<svg {width} height={800}>
|
||||
{#if xScale}
|
||||
<g transform={`translate(${margins.left},${margins.top})`}>
|
||||
{#if stackedMetrics.length > 0 && areaGenerator}
|
||||
{#each stackedMetrics as serie}
|
||||
<path d={areaGenerator(serie)} stroke={'white'} stroke-width={1} fill={colorScale(serie.key)}>
|
||||
</path>
|
||||
{/each}
|
||||
{/if}
|
||||
</g>
|
||||
{/if}
|
||||
</svg>
|
||||
{#if showTooltip}
|
||||
<Tooltip {tooltipX} {tooltipY} {tooltipContent} />
|
||||
{/if}
|
||||
|
||||
@ -28,8 +28,8 @@
|
||||
$: displayDataAs = isMobile ? "Cards" : "Table"
|
||||
|
||||
let cases = [];
|
||||
|
||||
let events = [];
|
||||
let metrics = [];
|
||||
|
||||
onMount(async function () {
|
||||
const response = await csv(`https://fiat-2024-processed-data.s3.us-west-2.amazonaws.com/Demo_Attribution_Data.csv`);
|
||||
@ -60,7 +60,21 @@
|
||||
events.forEach(d => {
|
||||
d.date = new Date(d.Date)
|
||||
})
|
||||
|
||||
|
||||
const metricsResponse = await csv('https://fiat-2024-processed-data.s3.us-west-2.amazonaws.com/fiat_country_metrics.csv')
|
||||
//metrics = metricsResponse
|
||||
//console.log(metrics)
|
||||
metrics = metricsResponse.map(d => {
|
||||
let obj = {}
|
||||
obj.date = new Date(d.Date),
|
||||
obj.posts = +d.Posts
|
||||
obj.country = d.Country
|
||||
return obj
|
||||
}
|
||||
)
|
||||
metrics.sort((a, b) =>{
|
||||
return a.date - b.date
|
||||
})
|
||||
});
|
||||
|
||||
$: if (cases) {
|
||||
@ -127,7 +141,7 @@
|
||||
{#if isMobile}
|
||||
<TimelineMobile {cases}></TimelineMobile>
|
||||
{:else}
|
||||
<Timeline {cases} {events}></Timeline>
|
||||
<Timeline {cases} {events} {metrics}></Timeline>
|
||||
{/if}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Загрузка…
x
Ссылка в новой задаче
Block a user