Skip to content

Instantly share code, notes, and snippets.

@w8r
Forked from mbostock/.block
Created August 21, 2017 11:38
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 w8r/2a79bfd4647eca6795543a0cf18409d7 to your computer and use it in GitHub Desktop.
Save w8r/2a79bfd4647eca6795543a0cf18409d7 to your computer and use it in GitHub Desktop.
Bonne
license: gpl-3.0
height: 900
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://unpkg.com/d3"></script>
<script src="https://unpkg.com/d3-geo-projection"></script>
<script src="https://unpkg.com/topojson"></script>
<style>
body {
background: #fcfcfa;
padding: 0;
margin: 0;
}
.stroke {
fill: none;
stroke: #000;
stroke-width: 3px;
}
.fill {
fill: #fff;
}
.graticule {
fill: none;
stroke: #777;
stroke-width: .5px;
stroke-opacity: .5;
}
.land {
fill: #222;
}
.boundary {
fill: none;
stroke: #fff;
stroke-width: .5px;
}
.fool {
background-image: url('bouffon.jpg');
background-position: center top;
background-repeat: no-repeat;
background-size: contain;
display: block;
position: absolute;
top: 0;
}
.map {
position: relative;
}
</style>
</head>
<body>
<script src="index.js"></script>
</body>
</html>
const height = document.documentElement.clientHeight;
const width = document.documentElement.clientWidth - 30;
const imageUrl = 'bouffon.jpg';
const projection = d3.geoBonne()
.center([0, 27])
.parallel(15)
.scale(60)
.translate([width / 2, height / 2])
.precision(0.2);
const path = d3.geoPath()
.projection(projection);
const graticule = d3.geoGraticule();
var canvas, ctx, cw, cx, cpath, cprojection,
world, land, boundary;
// Given a 4x4 perspective transformation matrix, and a 2D point (a 2x1 vector),
// applies the transformation matrix by converting the point to homogeneous
// coordinates at z=0, post-multiplying, and then applying a perspective divide.
function project(m, p) {
p = multiply(m, [p[0], p[1], 0, 1]);
return [p[0] / p[3], p[1] / p[3]];
}
// Post-multiply a 4x4 matrix in column-major order by a 4x1 column vector:
// [ m0 m4 m8 m12 ] [ v0 ] [ x ]
// [ m1 m5 m9 m13 ] * [ v1 ] = [ y ]
// [ m2 m6 m10 m14 ] [ v2 ] [ z ]
// [ m3 m7 m11 m15 ] [ v3 ] [ w ]
function multiply(m, v) {
return [
m[0] * v[0] + m[4] * v[1] + m[8 ] * v[2] + m[12] * v[3],
m[1] * v[0] + m[5] * v[1] + m[9 ] * v[2] + m[13] * v[3],
m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14] * v[3],
m[3] * v[0] + m[7] * v[1] + m[11] * v[2] + m[15] * v[3]
];
}
const img = new Image();
img.onload = () => {
const { naturalWidth, naturalHeight } = img;
const imageWidth = Math.min(width, naturalWidth);
const imageHeight = naturalHeight * (naturalWidth / imageWidth);
const background = d3.select('body')
.insert('div', '.map')
.classed('fool', true)
.style('background-image', `url(${imageUrl})`)
.style('width', imageWidth + 'px')
.style('height', imageHeight + 'px');
cw = imageWidth * 2; ch = imageHeight * 2;
canvas = background
.append('canvas')
.classed('map', true)
.attr('width', cw)
.attr('height', ch)
.style('width', cw / 2 + 'px')
.style('height', ch / 2 + 'px');
cprojection = d3.geoBonne()
.center([0, 27])
.parallel(17)
.scale(220)
.translate([cw / 2, ch / 2 - 270])
//.fitSize([cw, ch])
.precision(1);
ctx = canvas.node().getContext('2d');
ctx.lineJoin = 'round';
ctx.lineCap = 'round';
cpath = d3.geoPath()
.projection(cprojection)
.context(ctx);
};
img.src = imageUrl;
const svg = d3.select('body').append('svg')
.attr('width', width)
.attr('height', height);
svg.append('defs').append('path')
.datum({type: 'Sphere'})
.attr('id', 'sphere')
.attr('d', path);
svg.append('use')
.attr('class', 'stroke')
.attr('xlink:href', '#sphere');
svg.append('use')
.attr('class', 'fill')
.attr('xlink:href', '#sphere');
const graticuleSvg = svg.append('path')
.datum(graticule)
.attr('class', 'graticule')
.attr('d', path);
d3.json('world-50m.json', function(error, data) {
if (error) throw error;
world = topojson.presimplify(data);
world = topojson.simplify(world, 0.5);
const landSvg = svg.insert('path', '.graticule')
.datum(topojson.feature(world, world.objects.land))
.attr('class', 'land')
.attr('d', path);
land = topojson.feature(world, world.objects.land);
boundary = topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; });
const velocity = 0.02;
const svgBoundary = svg.insert('path', '.graticule')
.datum(boundary)
.attr('class', 'boundary')
.attr('d', path);
d3.timer(function(elapsed) {
if (!ctx) return;
ctx.clearRect(0, 0, cw, ch);
cprojection.rotate([velocity * elapsed, 0]);
ctx.beginPath();
cpath(graticule.outline());
ctx.strokeStyle = '#000000';
ctx.lineWidth = 1;
ctx.fillStyle = '#ffffff';
ctx.fill();
ctx.stroke();
ctx.beginPath();
cpath(land);
ctx.fillStyle = '#222222';
ctx.fill();
ctx.beginPath();
cpath(boundary);
ctx.strokeStyle = '#ffffff';
ctx.lineWidth = 1;
ctx.closePath();
ctx.stroke();
ctx.beginPath();
cpath(graticule());
ctx.strokeStyle = '#000000';
ctx.lineWidth = 0.2;
ctx.stroke();
});
});
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment