block by Fil 76ec54546d0c7ce9a847ccd3ab7d58b3

Perspective Map (transformed projection)

Full Screen

Using d3 v4 geo pipeline to skew and tweak a projection.

Uising dat.gui for easy parameters modifications.

Based on @mbostock’s following blocks:

index.html

<!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 -->