To dev:
yarn && yarn run start
Edit script.js and the page will update without a full reload.
Trying to remake Hurricane Irma Is One of the Strongest Storms In History with regl using line examples and rreusser’s demo. See my first attempt for an example of just drawing lines to different points.
I’m not sure if converting to the ndarray is necessary.
Hurricane data only includes storms through 2016 and hasn’t been cleaned.
forked from 1wheel‘s block: regl-hurricane-II
try { window.regltick.cancel() } catch(e){}
console.clear()
var data, byStorm
// parse hurricane data
d3.loadData('hurdat2.csv', (err, res) => {
data = res[0]
data.forEach((d, i) => {
if (d.Longitude.includes('E')){
d.Longitude = -d.Longitude.replace('E', '') + ''
}
if (d.Latitude.includes('S')){
d.Latitude = -d.Latitude.replace('S', '') + ''
}
d.i = i
})
byStorm = d3.nestBy(data, d => d.Serial)
byStorm.forEach(storm => {
storm.forEach((d, i) => {
d.maxWind = +d['Max wind']
d.day = i/4 // assumes data points a day
d.prev = i ? storm[i - 1] : null;
})
storm.maxWind = d3.max(storm, d => d.maxWind)
storm.forEach(d => {
d.color = storm.maxWind > 136 ? 1 : .1
})
// https://en.wikipedia.org/wiki/Accumulated_cyclone_energy
var ace = 0
storm.forEach(d => {
ace += d.maxWind*d.maxWind/10000
d.ace = ace
})
storm.ace = ace
})
byStorm = _.sortBy(byStorm, d => -d.maxWind)
// calc positions for each step
var projection = d3
.geoConicConformal()
.parallels([18 + 2 / 60, 18 + 26 / 60])
.rotate([60 + 26 / 60, 13 - 50 / 60])
.fitExtent([[-.7, .5], [0, .6]], {
'type': 'LineString', 'coordinates': [[-95.2, 0.5], [-60.1, 8.5]]
})
var x = d3.scaleLinear().domain([0, 16]).range([1, -1])
var windSpeedY = d3.scaleLinear().domain([40, 190]).range([-.6, 1])
var aceScaleY = d3.scaleLinear().domain([0, 90]).range([-.6, 1])
data.forEach(d => {
// position
d.pos0 = projection([-d.Longitude.replace('W', ''), d.Latitude.replace('N', '')])
// lon v. wind speed
d.pos1 = [d.pos0[0], windSpeedY(d.maxWind)]
// day v. wind speed
d.pos2 = [x(d.day), windSpeedY(d.maxWind)]
// day v. ACE
d.pos3 = [x(d.day), aceScaleY(d.ace)]
})
window.regl ? run(null, regl) : reglLib({onDone: run})
})
function run(err, regl){
window.regl = regl
// regl.clear({color: [0, 0, 0, 1], depth: 1})
var n = data.length
elements = byStorm
.map(d => d.filter(d => d.prev).map(d => [d.prev.i, d.i]))
.filter(d => d.length)
elements = _.flatten(elements)
var datasets = []
var curIndex = 0
var lastSwitchTime = 0
var switchInterval = 2
var switchDuration = 1
function createDatasets(){
// datasets = ['pos0', 'pos1', 'pos2', 'pos3'].map(str =>
datasets = ['pos3', 'pos0', 'pos1', 'pos2'].map(str =>
regl.buffer(vectorFill(ndarray([], [data.length, 2]), i => data[i][str]))
)
}
createDatasets()
var colors = linspace(ndarray([], [n]), 1, 0)
var drawPoints = regl({
vert: `
precision mediump float;
attribute vec2 xy0, xy1;
attribute float basis;
varying float c;
uniform float interp;
void main() {
c = basis;
vec2 pos = mix(xy0, xy1, interp);
gl_Position = vec4(pos, 0, 1);
}`,
frag: `
precision mediump float;
varying float c;
void main() {
gl_FragColor = vec4(c, 0, c, c + .1);
}`,
lineWidth: 1,
attributes: {
xy0: () => datasets[curIndex % datasets.length],
xy1: () => datasets[(curIndex + 1) % datasets.length],
basis: data.map(d => d.color),
},
uniforms: {
interp: (ctx, props) => Math.max(0, Math.min(1, props.interp))
},
elements: regl.elements({
data: elements,
type: 'uint16',
usage: 'static',
primitive: 'lines'
}),
})
window.regltick = regl.frame(({time}) => {
// Check how long it's been since the last switch, and cycle the buffers
// and reset the timer if it's time for a switch:
if ((time - lastSwitchTime) > switchInterval) {
lastSwitchTime = time
curIndex++
}
drawPoints({interp: ease((time - lastSwitchTime) / switchDuration)})
})
}
<!DOCTYPE html>
<html>
<head>
<body></body>
<script src='d3+_.js'></script>
<script src='lib-build.js'></script>
<script src='_script.js'></script>
</html>
var libs = {
glsl: require('glslify'),
linspace: require('ndarray-linspace'),
vectorFill: require('ndarray-vector-fill'),
ndarray: require('ndarray'),
ease: require('eases/cubic-in-out'),
reglLib: require('regl'),
}
var glsl = require('glslify')
libs.glslViridis = glsl`
#pragma glslify: colormap = require(glsl-colormap/viridis)
`
for (key in libs) window[key] = libs[key]
{
"version": "1.0.0",
"description": "watchify and hot-server",
"scripts": {
"start": "watchify -t glslify lib-src.js -o lib-build.js & hot-server"
},
"dependencies": {
"browserify": "^14.1.0",
"budo": "^9.4.7",
"control-panel": "^1.2.0",
"d3": "^4.10.2",
"eases": "^1.0.8",
"es2040": "^1.2.5",
"fail-nicely": "^2.0.0",
"github-cornerify": "^1.0.7",
"glsl-colormap": "^1.0.1",
"glsl-noise": "^0.0.0",
"glslify": "^6.0.1",
"hot-server": "^0.0.11",
"indexhtmlify": "^1.3.1",
"metadataify": "^1.0.3",
"ndarray": "^1.0.18",
"ndarray-linspace": "^2.0.3",
"ndarray-vector-fill": "^1.0.0",
"regl": "^1.3.0",
"standard": "^9.0.1",
"uglify-js": "^2.8.13",
"watchify": "^3.9.0"
}
}