Skip to content

Instantly share code, notes, and snippets.

@flekschas
Last active September 14, 2020 13:23
Show Gist options
  • Save flekschas/81e7fb4f9cf5946f12639c383af97dc6 to your computer and use it in GitHub Desktop.
Save flekschas/81e7fb4f9cf5946f12639c383af97dc6 to your computer and use it in GitHub Desktop.
Ridge Plots
{
"title": "Ridge Plots",
"subtitle": "Global Surface Temperature",
"thumbnail": "https://user-images.githubusercontent.com/932103/92788343-dcd7a800-f377-11ea-9077-f519eac82a62.jpg"
}
/* Load and wrangle the data in this file */
import { line } from 'd3-shape';
import { REL_HEIGHT } from './styles';
const FROM_X = -1.5;
const TO_X = 1.5;
const NUM_STEPS = 50;
const TICK_HEIGHT = 3;
const MONTHS = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec',
];
const FILL_COLOR_RANGE = ['#3170ad', '#cccccc', '#c76526'];
const STROKE_COLOR_RANGE = ['#1d4266', '#333333', '#663413'];
const START_YEAR = 1880;
const NUM_YEARS_PER_STEP = 10;
// Derived
const DOMAIN_SIZE = TO_X - FROM_X;
const STEP_SIZE = DOMAIN_SIZE / NUM_STEPS;
const ABS_HEIGHT = 100 * REL_HEIGHT;
const createSvgStart = () =>
`<svg viewBox="0 0 100 ${ABS_HEIGHT + TICK_HEIGHT}" xmlns="http://www.w3.org/2000/svg">`;
const createGradient = (name, startColor, midColor, endColor) => `<defs>
<linearGradient id="${name}" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="${startColor}"/>
<stop offset="50%" stop-color="${midColor}"/>
<stop offset="100%" stop-color="${endColor}"/>
</linearGradient>
</defs>`;
const createSvgEnd = () => '</svg>';
const createLine = (barWidth) => line()
.x((_, i) => (barWidth / 2) + barWidth * i)
.y((d) => ABS_HEIGHT - ABS_HEIGHT * d);
const createPath = (kde, barWidth) => {
const path = createLine(barWidth)(kde);
return `<path d="M${ABS_HEIGHT},0${path}L100,${ABS_HEIGHT}L" stroke="url(#linear-stroke)" stroke-size="1" fill="url(#linear-fill)"/>`;
};
const createTitle = (titleLeft, titleRight, avgTemp) =>
`<foreignObject x="1" y="${ABS_HEIGHT - 9}" width="100" height="12">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; justify-content: space-between; font: 8px sans-serif; color: #999">
<span>${titleLeft} ${avgTemp > 0 ? titleRight : ''}</span>
<span>${avgTemp <= 0 ? titleRight : ''}</span>
</div>
</foreignObject>`;
const createAxis = (ticks) => {
const tickEls = ticks.map((tick) => {
const x = ((tick - FROM_X) / DOMAIN_SIZE) * 100;
return `<line x1="${x}" y1="0" x2="${x}" y2="${TICK_HEIGHT}" stroke="black" opacity="0.33" />`;
});
return `<g transform="translate(0 ${ABS_HEIGHT})">${tickEls.join('')}</g>`;
};
const getAvgTemp = (hist) =>
FROM_X + (hist.findIndex((x) => x === 1) + 0.5) * STEP_SIZE;
const createLinePlot = (hist, years, month, barWidth) => [
createSvgStart(),
createGradient('linear-fill', ...FILL_COLOR_RANGE),
createGradient('linear-stroke', ...STROKE_COLOR_RANGE),
createTitle(MONTHS[month], years, getAvgTemp(hist)),
createPath(hist, barWidth),
createAxis([-1, 0, 1]),
createSvgEnd()
].join('');
export default async function data() {
const response = await fetch('https://storage.googleapis.com/pilingjs/global-surface-temperature/data.json');
const data = await response.json();
const numBins = data[0][0].length;
const barWidth = 100 / numBins;
return data.flatMap((kdes, numMonth) => {
return kdes.map((kde, numDecade) => {
const years = `${START_YEAR + NUM_YEARS_PER_STEP * numDecade}s`;
return {
kde,
src: createLinePlot(kde, years, numMonth, barWidth),
decade: years,
month: MONTHS[numMonth],
numMonth,
numDecade,
};
})
});
}
import { createSvgRenderer } from 'piling.js';
import { COLUMNS, REL_HEIGHT } from './styles';
export default function renderers({ domElement }) {
const { width } = domElement.getBoundingClientRect();
const itemWidth = (width / COLUMNS) * 3;
const itemHeight = itemWidth * REL_HEIGHT;
const itemRenderer = createSvgRenderer({
width: itemWidth,
height: itemHeight,
color: '#ccc',
});
return {
itemRenderer
};
}
export const REL_HEIGHT = 0.4; // Used by the renderer and data
export const COLUMNS = 12; // Used by the renderer
const styles = {
cellAspectRatio: 1 / REL_HEIGHT,
pileCellAlignment: 'center',
columns: COLUMNS,
pileItemOffset: [0, 8],
cellPadding: 4,
pileItemBrightness: (_, i, pile) =>
Math.min(0.5, 0.01 * (pile.items.length - i - 1)),
pileScale: (pile) => 1 + Math.min(0.5, (pile.items.length - 1) * 0.1),
pileOrderItems: (pile) => [...pile.items].sort((a, b) => a - b),
};
export default styles;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment