Skip to content

Instantly share code, notes, and snippets.

@Fil
Last active March 3, 2018 20:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Fil/880a2973f5416ddca696abc484eca3ba to your computer and use it in GitHub Desktop.
Save Fil/880a2973f5416ddca696abc484eca3ba to your computer and use it in GitHub Desktop.
Projection Paint [UNLISTED]
license: gpl-3.0
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="https://unpkg.com/d3@4"></script>
<script src="https://unpkg.com/d3-geo-projection"></script>
<script src="https://unpkg.com/topojson"></script>
<script>
var width = 960,
height = 500;
var radius = height / 2 - 20,
scale = radius,
velocity = .015;
var projection = d3.geoOrthographic()
.fitExtent([[0,0],[300,300]], {type:"Sphere"});
var canvas = d3.select("body").append("canvas")
.attr("width", width)
.attr("height", height);
var context = canvas.node().getContext("2d");
// retina display
var devicePixelRatio = window.devicePixelRatio || 1;
canvas.style('width', canvas.attr('width')+'px');
canvas.style('height', canvas.attr('height')+'px');
canvas.attr('width', canvas.attr('width') * devicePixelRatio);
canvas.attr('height', canvas.attr('height') * devicePixelRatio);
context.scale(devicePixelRatio,devicePixelRatio);
var path = d3.geoPath()
.projection(projection)
.context(context);
var backprojection = d3.geoProjection(function(a,b) {
return d3.geoOrthographicRaw(-a,b);
}).clipAngle(90)
.translate(projection.translate())
.scale(projection.scale())
var backpath = d3.geoPath()
.projection(backprojection)
.context(context);
var projection2, path2;
var projname = 'Bertin1953';
var projs = ['Bertin1953', 'Mercator', 'Gnomonic', 'Equirectangular'];
newproj = function() {
projection2 = d3['geo' + projname]().fitExtent([[300,0],[960,500]], {type:"Sphere"});
path2 = d3.geoPath()
.projection(projection2)
.context(context);
projname = projs[Math.random() * projs.length | 0];
};
newproj()
d3.json("https://unpkg.com/world-atlas/world/110m.json", function(error, world) {
if (error) throw error;
var land = topojson.feature(world, world.objects.land);
var points = (d3.merge(d3.merge(land.features[0].geometry.coordinates)))
.map((d,i) => (d.rank = i, d))
//.filter((d,i)=> i%2)
// random order
//points = points.sort((a,b) => Math.random() - Math.random())
// by longitude
//points = points.sort((a,b) => -a[0]+b[0])
// by latitude
//points = points.sort((a,b) => -a[1]+b[1])
points.forEach(function(point) {
point.vecteur = Math.random() < 0.04;
})
render = function(elapsed) {
context.clearRect(0, 0, width, height);
var rotate = [velocity * elapsed, -40];
// if (elapsed > 10000) projection2 = d3.geoEquirectangular().fitExtent([[300,0],[960,500]], {type:"Sphere"}), path2 = d3.geoPath().projection(projection2);
projection.rotate(rotate);
if (projection2.rotate) projection2.rotate([rotate[0], 0]);
var t_intro = 3000,
t_projection = 5000,
t_lancer = 2800,
t_pleine = t_lancer + 3000,
t_disparition = 1500;
var periode = (t_intro + t_projection + t_lancer + t_pleine + t_disparition),
N = points.length,
t = elapsed % periode;
if (t < 40) newproj();
var tt = (t - (t_intro + t_projection + t_lancer))/(t_pleine + t_disparition);
if (tt > 0 && tt < 1) {
context.beginPath();
path2(land);
context.fillStyle = '#f4f4f4f4';
context.globalAlpha = Math.min(1, 7 * tt * (1-tt));
context.fill();
context.globalAlpha = 1;
}
points.forEach(function(point,i){
point.A = projection(point);
point.B = projection2(point);
point.segment = [point.A,point.B];
point.bout = 1;
point.ti = t - i / N * t_projection - t_intro;
point.td = t - i / N * t_projection * 0.5 - t_intro;
if (point.ti < 0) return point.bout = 0;
if (point.ti > t_projection + t_lancer + t_pleine + t_disparition) return point.bout = 0;
if (point.ti < 0.4 * t_lancer) {
var p = 1 - point.ti / (0.4 * t_lancer),
C = [p * point.A[0] + (1-p) * point.B[0], p * point.A[1] + (1-p)*point.B[1]];
point.segment = [point.A,C];
point.bout = 0.5;
} else if (point.ti < 0.7 * t_lancer) {
var p = -(point.ti - 0.7 * t_lancer) / ((0.7 - 0.4) * t_lancer),
C = [p * point.A[0] + (1-p) * point.B[0], p * point.A[1] + (1-p)*point.B[1]];
point.segment = [C,point.B];
point.bout = 1;
}
else if (point.td < t_lancer + t_pleine) {
point.segment = [point.B,point.B];
point.bout = 1;
}
else {
// disparition
point.segment = [point.B,point.B];
point.bout = Math.max(0,1 - (point.td - t_lancer - t_pleine) / t_disparition);
}
});
points.forEach(point => {
var p = projection.invert(projection(point));
point.derriere = (d3.geoDistance(p, point) > 0.01);
})
context.beginPath();
points.filter(point => point.bout)
.filter(point => point.vecteur)
.filter(point => point.derriere)
.forEach(function(point){
context.moveTo(...point.segment[0]);
context.lineTo(...point.segment[1]);
});
context.lineWidth = .5;
context.strokeStyle = 'rgba(100,100,100,0.6)';
context.stroke();
backprojection.rotate([rotate[0] + 180, -rotate[1]]);
context.beginPath();
path({type:"Sphere"});
context.fillStyle = 'white';
context.fill();
context.beginPath();
backpath(land);
context.fillStyle = '#f4f4f4f4';
context.fill();
context.beginPath();
path(d3.geoGraticule()());
path2(d3.geoGraticule()());
context.lineWidth = 0.05;
context.strokeStyle = 'black';
context.stroke();
context.beginPath();
path(land);
//path2(land);
context.lineWidth = 1;
context.strokeStyle = 'black';
context.stroke();
context.fillStyle = '#eee';
context.fill();
context.beginPath();
points.filter(point => point.bout)
.filter(point => point.vecteur)
.filter(point => !point.derriere)
.forEach(function(point){
context.moveTo(...point.segment[0]);
context.lineTo(...point.segment[1]);
});
context.lineWidth = .5;
context.strokeStyle = 'rgba(100,100,100,0.7)';
context.stroke();
context.beginPath();
var last = [0,0];
points
.slice()
.sort((a,b) => a.rank - b.rank)
.filter(point => point.bout).forEach(function(point){
context.lineWidth = 1;
// context.moveTo(...point.segment[1]);
// context.lineTo(point.segment[1][0]+point.bout*2, point.segment[1][1]+point.bout);
var p = point.segment[1];
if (Math.max(Math.abs(p[0] - last[0]), Math.abs(p[1] - last[1])) < 10)
context.lineTo(p[0], p[1]);
else {
context.moveTo(p[0]-1, p[1]);
context.lineTo(p[0], p[1]);
}
last = p;
})
context.lineWidth = 1.;
context.strokeStyle = '#333';
context.stroke();
context.beginPath();
path({type: "Sphere"});
path2({type: "Sphere"});
context.lineWidth = 1.;
context.strokeStyle = 'black';
context.stroke();
};
render(0);
d3.timer(render)
});
d3.select(self.frameElement).style("height", height + "px");
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment