Last active
September 14, 2020 13:23
-
-
Save flekschas/81e7fb4f9cf5946f12639c383af97dc6 to your computer and use it in GitHub Desktop.
Ridge Plots
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"title": "Ridge Plots", | |
"subtitle": "Global Surface Temperature", | |
"thumbnail": "https://user-images.githubusercontent.com/932103/92788343-dcd7a800-f377-11ea-9077-f519eac82a62.jpg" | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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, | |
}; | |
}) | |
}); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
}; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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