Built with blockbuilder.org
<!DOCTYPE html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- <meta name="viewport" content="user-scalable = yes"> -->
<title>A Breathing Earth - Minimal Regl version</title>
<meta name="author" content="Nadieh Bremer">
<!-- JavaScript files -->
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="regl.min.js"></script>
<!-- Styling -->
<style>
body {
text-align: center;
font-family: 'Source Code Pro', monospace;
}
#container {
width: 1000px;
margin-left: auto;
margin-right: auto;
text-align: center;
padding-left: 20px;
padding-right: 20px;
}
#chart {
text-align: center;
margin-left: auto;
margin-right: auto;
box-shadow: 0px 0px 5px #c1c1c1;
}
#week {
text-align: left;
font-size: 18px;
margin-top: 15px;
margin-bottom: 10px;
}
</style>
<!-- Google fonts -->
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:200,300,400" rel="stylesheet">
</head>
<body>
<div id="container">
<div id="week"></div>
<div id="chart">
<canvas id="canvas"></canvas>
</div>
</div>
<script src="main.js"></script>
</body>
</html>
createReglMap();
//Function to draw the regl based map
function createReglMap() {
///////////////////////////////////////////////////////////////////////////
//////////////////////////// Set up the canvas ////////////////////////////
///////////////////////////////////////////////////////////////////////////
var width = 2000;
//Mercator map ratio
var mapRatio = 0.5078871;
var height = Math.round(mapRatio * width);
var canvas = document.getElementById("canvas");
canvas.width = width;
canvas.height = height;
//Get the visible size back to 1000 px
canvas.style.width = (width*0.5) + 'px';
canvas.style.height = (height*0.5) + 'px';
//canvas.getContext("2d").scale(0.5, 0.5);
var regl = createREGL({
extensions: ['OES_standard_derivatives'],
canvas: canvas,
attributes: {
alpha: false,
depth: false,
antialias: true
}
});
regl.clear({color: [1, 1, 1, 1]});
///////////////////////////////////////////////////////////////////////////
///////////////////////// Create global variables /////////////////////////
///////////////////////////////////////////////////////////////////////////
const nWeeks = 52; //Number of weeks in the year
var mapPoints; //Will save the coordinate mapping
//The minimum and maximum values of the layer variable
const maxL = 0.8,
minL = 0; //-0.06;
///////////////////////////////////////////////////////////////////////////
/////////////////////////////// Create scales /////////////////////////////
///////////////////////////////////////////////////////////////////////////
var xScale = d3.scaleLinear()
.domain([1, 500])
.range([-1, 1]);
var yScale = d3.scaleLinear()
.domain([1, 250])
.range([-1,1]);
var radiusScale = d3.scaleSqrt()
.domain([minL, maxL])
.range([0, 5])
.clamp(true);
var opacityScale = d3.scaleLinear()
.domain([minL, maxL])
.range([1, 0.5]);
var greenColor = d3.scaleLinear()
.domain([-0.08, 0.1, maxL])
//.range(["#FFBE1F", "#d9e537", "#054501"])
.range(["#FAECAB", "#f2ec82", "#0c750c"])
//.clamp(true);
//Wrap d3 color scales so they produce vec3s with values 0-1
function wrapColorScale(scale) {
return function(t) {
const rgb = d3.rgb(scale(t));
return [rgb.r / 255, rgb.g / 255, rgb.b / 255];
};
}//wrapColorScale
var greenColorRGB = wrapColorScale(greenColor);
///////////////////////////////////////////////////////////////////////////
/////////////////////////// Map drawing settings //////////////////////////
///////////////////////////////////////////////////////////////////////////
function createMapMaker() {
var drawMap = regl({
vert: `
precision highp float;
//The progess along the transition
uniform float progress;
//Needed to calculate fopacity
attribute float currOpacity;
attribute float nextOpacity;
//Needed to set gl_Pointsize
attribute float currSize;
attribute float nextSize;
//Needed to set gl_Position
attribute vec2 position;
//Needed to set fcolor
attribute vec3 currColor;
attribute vec3 nextColor;
//Will get values used in the fragment shader
varying float fopacity;
//varying float fsize;
varying vec3 fcolor;
void main () {
fcolor = mix(currColor, nextColor, progress);
fopacity = mix(currOpacity, nextOpacity, progress);
//fsize = size/7.5;
gl_PointSize = mix(currSize, nextSize, progress);
gl_Position = vec4(position, 0, 1);
}
`,
frag: `
precision highp float;
#ifdef GL_OES_standard_derivatives
#extension GL_OES_standard_derivatives : enable
#endif
varying float fopacity;
//varying float fsize;
varying vec3 fcolor;
void main (){
//Creating a circle out of a square:
//Determine normalized distance from center of point
float point_dist = length(gl_PointCoord * 2. - 1.);
//Based in part on:
//http://bl.ocks.org/monfera/85aa9627de1ae521d3ac5b26c9cd1c49
//http://www.numb3r23.net/2015/08/17/using-fwidth-for-distance-based-anti-aliasing
//https://www.desultoryquest.com/blog/drawing-anti-aliased-circular-points-using-opengl-slash-webgl
#ifdef GL_OES_standard_derivatives
//Anti-aliasing-factor
float aaf = fwidth(point_dist)/2.0;
//if(point_dist + aaf > 1.0) discard; //strangley this doesn't work as inteded at all
float alpha = fopacity*(1.0 - smoothstep(1.0 - aaf, 1.0 + aaf, point_dist));
#else
if(point_dist > 1.0) discard;
float alpha = fopacity;
#endif
//From:https://gist.github.com/rflow/39692bd181fb1eb0b077a4caf886b077
//But seems to discard too much for smaller circles
//Calc scale at which to start fading out the circle
//float min_dist = fsize * 0.7;
//Calc scale at which we find the edge of the circle
//float max_dist = fsize;
//From https://thebookofshaders.com/glossary/?search=smoothstep
//float alpha = fopacity * (1.0 - smoothstep(min_dist, max_dist, point_dist));
//The most basic way to get a (pixelated) circle
//if (point_dist > 1.0) discard;
//gl_FragColor = vec4(alpha*fcolor, alpha*fopacity);// - correct for no multiply but with pre-opacity multiplication (& the //2 func below)
//gl_FragColor = vec4(fcolor, alpha*fopacity); // correct for nu multiply and without pre-opacity multiplication (& the //1 func below)
// premultiplying the alpha like this means we can use *just* the multiplicative
// part of the blending function (src * dst). But alpha = 0 corresponds to
// multiplying by one, so we have to do this little 'one minus' trick:
gl_FragColor = vec4(1.0 - alpha * (1.0 - fcolor), 1);
}//void main
`,
depth: {enable: false, mask: false},
blend: {
enable: true,
//func: { srcRGB: 'src alpha', dstRGB: 'one minus src alpha', srcAlpha: 1, dstAlpha: 'one minus src alpha' }, //1
//func: {src: 1, dst: 'one minus src alpha'}, //2
func: { srcRGB: 'dst color', dstRGB: 0, srcAlpha: 1, dstAlpha: 1 },
//func: {src: 'one minus dst alpha', dst: 'src color'}, //never got this working correctly with opacities, but it does look like a multiply blend
//func: {src: 'dst color', dst: 'one minus src alpha'}, //seems to work for Pixi, but I just get a white screen...
equation: { rgb: 'add', alpha: 'add' },
},
attributes: {
position: mapPoints,
currColor: regl.prop('currColor'),
nextColor: regl.prop('nextColor'),
currOpacity: regl.prop('currOpacity'),
nextOpacity: regl.prop('nextOpacity'),
currSize: regl.prop('currSize'),
nextSize: regl.prop('nextSize'),
},
uniforms: {
progress: regl.prop('progress'),
},//uniforms
count: mapPoints.length,
primitive: 'points',
});//drawTriangle
return drawMap;
}//function createMapMaker
///////////////////////////////////////////////////////////////////////////
//////////////////////////// Read in the data /////////////////////////////
///////////////////////////////////////////////////////////////////////////
d3.queue()
.defer(d3.csv, "worldMap_coordinates.csv")
.defer(d3.csv, "mapData-week-22.csv")
.defer(d3.csv, "mapData-week-23.csv")
.await(drawFirstMap);
function drawFirstMap(error, coordRaw, data, data2) {
///////////////////////////////////////////////////////////////////////////
///////////////////////////// Final data prep /////////////////////////////
///////////////////////////////////////////////////////////////////////////
if (error) throw error;
//The locations of every map are the same, so save the in a variable
mapPoints = coordRaw.map(function(d) { return [xScale(+d.x), yScale(+d.y)]; });
//Prepare the map data
data.forEach(function(d) {
d.layer = +d.layer;
d.opacity = opacityScale(d.layer);
d.color = greenColorRGB(d.layer);
//Premultiply the colors by the alpha
// d.color[0] = d.color[0] * d.opacity;
// d.color[1] = d.color[1] * d.opacity;
// d.color[2] = d.color[2] * d.opacity;
d.size = radiusScale(d.layer);
});
//Create a new array that shuffles the data a bit
var currMap = {
colors: data.map(function(d) { return d.color; }),
opacities: data.map(function(d) { return d.opacity; }),
sizes: data.map(function(d) { return 2*d.size; }),
};
//Prepare the map data
data2.forEach(function(d) {
d.layer = +d.layer;
d.opacity = opacityScale(d.layer);
d.color = greenColorRGB(d.layer);
//Premultiply the colors by the alpha
// d.color[0] = d.color[0] * d.opacity;
// d.color[1] = d.color[1] * d.opacity;
// d.color[2] = d.color[2] * d.opacity;
d.size = radiusScale(d.layer);
});
//Create a new array that shuffles the data a bit
var nextMap = {
colors: data2.map(function(d) { return d.color; }),
opacities: data2.map(function(d) { return d.opacity; }),
sizes: data2.map(function(d) { return 2*d.size; }),
};
///////////////////////////////////////////////////////////////////////////
//////////////////////////// Draw the first map ///////////////////////////
///////////////////////////////////////////////////////////////////////////
//Create the regl function
var drawMap = createMapMaker();
//Adjust the title
d3.select("#week").text("Week " + 22 + ", " + "May"+ ", 2016");
regl.clear({color: [1, 1, 1, 1]});
//Draw the current map
drawMap({
currColor: currMap.colors,
nextColor: nextMap.colors,
currOpacity: currMap.opacities,
nextOpacity: nextMap.opacities,
currSize: currMap.sizes,
nextSize: nextMap.sizes,
progress: 0.0
});
}//function drawFirstMap
}//function createReglMap