Skip to content

Instantly share code, notes, and snippets.

@Fil
Last active Oct 31, 2018
Embed
What would you like to do?
Perspective Map (transformed projection)
license: gpl-3.0
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="//d3js.org/d3.v4.min.js"></script>
<script src="//d3js.org/d3-geo-projection.v1.min.js"></script><script src="//d3js.org/topojson.v1.min.js"></script>
<script>
var width = 960,
height = 500,
opts = {
scale: 250,
translate: { x: width/2 - 110, y: height/2 },
focusY: 4.4,
angle: 8,
scaleY: 0.55,
fillColor: 'rgba(78, 216, 158, 0.99)'
};
function draw(land, places){
// We use a scale 1 Robinson projection as base
// then tweak it (with different rescales on X and Y)
var baseprojection = d3.geoRobinson()
.rotate([-10.5, 0])
.translate([0, 0])
.scale(1)
.precision(0.001),
// maps the base-projected point to the screen coords
screenproject = function (p) {
var x = p[0] * (p[1] + opts.focusY)/opts.angle,
y = p[1] * opts.scaleY;
return [opts.scale * x + opts.translate.x, opts.scale * y + opts.translate.y];
},
// creates a geoTransform from the previous function
transform = d3.geoTransform({
point: function (x, y) {
var q = screenproject([x, y]);
if (q) this.stream.point(q[0], q[1]);
}
}),
// this so we can use the projection as a function([lon,lat])
projection = function (p) {
return screenproject(baseprojection(p));
};
// Here is where we compose the functions.
// it's `transform o baseprojection`, but coded the other way,
// as it works with callbacks (bleh!)
projection.stream = function (s) {
return baseprojection.stream(transform.stream(s));
};
var context = canvas.node().getContext("2d");
var path = d3.geoPath()
.projection(projection)
.context(context);
context.clearRect(0, 0, width, height);
context.beginPath();
context.strokeStyle ="#eee";
context.fillStyle ="#9ab";
context.lineWidth = 0.5;
path(land);
context.fill();
context.stroke();
context.beginPath();
path(places);
context.fillStyle = opts.fillColor;
context.lineWidth = 1.5;
context.strokeStyle = d3.rgb(opts.fillColor).darker();
context.fill();
context.stroke();
}
var canvas = d3.select("body").append("canvas")
.attr("width", width)
.attr("height", height);
d3.json("countries.topo.json", function(error, world) {
if (error) throw error;
var land = topojson.feature(world, world.objects.countries);
var places = {type: "MultiPoint",
coordinates: land.features
.filter(function(c){
return c.properties.pop_est > 50000000
})
.map(function(c){
return d3.geoCentroid(c) ;
})
};
function redraw(){
draw(land, places);
}
redraw();
if (typeof gui == 'function')
gui(opts, redraw, {
listen: true,
options: { width: 300 },
});
});
</script>
<script src="https://unpkg.com/dat.gui/build/dat.gui.min.js"></script>
<link rel="stylesheet" href="https://raw.githack.com/liabru/dat-gui-light-theme/master/dat-gui-light-theme.css"/>
<script>
dat.GUI.TEXT_CLOSED = '🏇';
dat.GUI.TEXT_OPEN = '💡';
function gui(opts, redraw, config) {
var gui = new dat.GUI(config.options || {});
gui.close();
for (var i in opts) {
add(gui, opts, i);
}
function add(src, o, t) {
if (typeof o[t] == 'object') {
var group = src.addFolder(t);
for (var j in o[t]) {
add(group, o[t], j);
}
} else {
var control = (t.match(/color/i))
? src.addColor(o, t)
: src.add(o, t);
if (config.listen) control.listen();
if (redraw) control.onChange(redraw);
}
}
}
</script>
<!-- /dat.gui -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment