Skip to content

Instantly share code, notes, and snippets.

@Fil
Last active October 31, 2018 13:29
Show Gist options
  • Select an option

  • Save Fil/76ec54546d0c7ce9a847ccd3ab7d58b3 to your computer and use it in GitHub Desktop.

Select an option

Save Fil/76ec54546d0c7ce9a847ccd3ab7d58b3 to your computer and use it in GitHub Desktop.
Perspective Map (transformed projection)
license: gpl-3.0
Display the source blob
Display the rendered blob
Raw
Loading
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