block by enjalot 27969219a945e2bd20dc

d3.geo.weichel

Full Screen

Admiring Jason Davies’ work on the Weichel projection

forked from enjalot‘s block: visualizing map distortion

index.html

<!DOCTYPE html>
<head>
  <meta charset="utf-8">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3-geo-projection/0.2.15/d3.geo.projection.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/1.6.19/topojson.min.js"></script>
<style>
  svg {
    margin: 22px;
  }
  select {
    margin-left: 20px;
  }
  path.foreground {
    fill: none;
    stroke: #333;
    stroke-width: 1.5px;
  }
  path.graticule {
    fill: none;
    stroke: #aaa;
    stroke-width: .5px;
  }
  #left {

  }
  #left .land {
    fill: #d7c7ad;
    stroke: #a5967e;
  }
  #right .land {
    fill: #cfcece;
    stroke: #a5967e;
  }
  
  #left circle {
    fill: #d8355e;
  }
  #right circle {
    stroke: #d8355e;
    fill: none;
  }
</style>
</head>

<body>
  <svg id="left"></svg>
  <svg id="right"></svg>
  
  <script>
    var map_width = 400;
    var map_height = 400;
    var center = [-90, 37];
    var scale0 = (map_width - 1) / 2 / Math.PI * 6
    var scale1 = (map_width - 1) / 2 / Math.PI * 3

    
    
    var projectionLeft = d3.geo.wiechel()
    //.center(center)
    .translate([map_width/2, map_height/2])
    .rotate([0, -90])
    .clipAngle(180 - 1)
    .precision(.1)
    .scale(90)
    .rotate([100, -90])
    
    var projectionRight = d3.geo.orthographic()
    .center(center)
    .translate([map_width/5, map_height / 5])
    .scale(scale1)
    .clipAngle(90)

    var pathLeft = d3.geo.path()
        .projection(projectionLeft);
    var pathRight = d3.geo.path()
        .projection(projectionRight);
    
    function zoomed() {

      var newCenter = projectionLeft.invert([map_width/2,map_height/2]);
      
      projectionRight
        .rotate([-newCenter[0], -newCenter[1]])

      update();
    }
    
    var start = Date.now();
    var duration = 20;
    var pause = false;
    var dir = 1;
    d3.timer(function(elapsed) {
      var now = Date.now();
      if(now - start > duration) {
        start = now;
      } else {
        return false;
      }
      var dt = elapsed * 0.00005 % 2*Math.PI
      //var deg = dt * 180/Math.PI * 0.1;
      //console.log(deg)
      var deg = 3
      var rot = projectionLeft.rotate()
      rot[0] = (rot[0] + deg)// % 360
      rot[1] = (rot[1] - deg)// % 360
      var scale = projectionLeft.scale();
      
      if(scale > 200 && dir === 1) {
        dir = -1;
      } else if(scale < 80 && dir === -1) {
        dir = 1;
      }
      if(dir === -1) {
        scale -= 5
      } else {
      	scale += 5;
      }
      projectionLeft.scale(scale)
      //console.log(rot)
			projectionLeft.rotate(rot)
      zoomed()
      
      return pause;
    })
    d3.select("body").on("click", function() {
      pause = true;
    })
    
    function update() {
      d3.selectAll("#left path")
          .attr("d", pathLeft);
      d3.selectAll("#right path")
          .attr("d", pathRight);
      
      d3.selectAll("#left circle")
      .attr({
        cx: function(d,i) { return d.x },
        cy: function(d,i) { return d.y }
      })
      
      d3.selectAll("#right circle")
      .attr({
        cx: function(d,i) {
          var latlon = projectionLeft.invert([d.x, d.y])
          return projectionRight(latlon)[0] 
        },
        cy: function(d,i) {
          var latlon = projectionLeft.invert([d.x, d.y])
          return projectionRight(latlon)[1]
        }
      })
    }
    
    

    var graticule = d3.geo.graticule();
    var svgLeft = d3.select("#left")
        .attr("width", map_width)
        .attr("height", map_height);
    var svgRight = d3.select("#right")
        .attr("width", map_width + 40)
        .attr("height", map_height);
   

    svgLeft.append("path")
        .datum(graticule)
        .attr("class", "graticule")
        .attr("d", pathLeft);
    
    svgRight.append("path")
        .datum(graticule)
        .attr("class", "graticule")
        .attr("d", pathRight);

    d3.json("world-110m.json", function(error,world) {
      if (error) throw error;

      svgLeft.insert("path", ".graticule")
          .datum(topojson.feature(world, world.objects.land))
          .attr("class", "land")
          .attr("d", pathLeft);
      
      svgRight.insert("path", ".graticule")
          .datum(topojson.feature(world, world.objects.land))
          .attr("class", "land")
          .attr("d", pathRight);
      
      var points = generateRect(500, 75, 75, map_width - 150, map_height - 150);
      
      svgLeft.selectAll("circle")
        .data(points)
        .enter().append("circle")
        .attr({
          r: 3
        })
      
      svgRight.selectAll("circle")
        .data(points)
        .enter().append("circle")
        .attr({
          r: 2
        })
      
      zoomed();
    });
    
    var projections = {
      "Aitoff": d3.geo.aitoff().scale(90),
      "Boggs Eumorphic": d3.geo.boggs().scale(90),
      "Craster Parabolic (Putnins P4)": d3.geo.craster().scale(90),
      "Cylindrical Equal-Area": d3.geo.cylindricalEqualArea().scale(120),
      "Eckert I": d3.geo.eckert1().scale(95),
      "Eckert III": d3.geo.eckert3().scale(105),
      "Eckert IV": d3.geo.eckert4().scale(105),
      "Eckert V": d3.geo.eckert5().scale(100),
      "Equidistant Cylindrical (Plate Carrée)": d3.geo.equirectangular().scale(90),
      "Fahey": d3.geo.fahey().scale(75),
      "Foucaut Sinusoidal": d3.geo.foucaut().scale(80),
      "Gall (Gall Stereographic)": d3.geo.cylindricalStereographic().scale(70),
      "Ginzburg VIII (TsNIIGAiK 1944)": d3.geo.ginzburg8().scale(75),
      "Kavraisky VII": d3.geo.kavrayskiy7().scale(90),
      "Larrivée": d3.geo.larrivee().scale(55),
      "McBryde-Thomas Flat-Pole Sine (No. 2)": d3.geo.mtFlatPolarSinusoidal().scale(95),
      "Mercator": d3.geo.mercator().scale(50),
      "Miller Cylindrical I": d3.geo.miller().scale(60),
      "Mollweide": d3.geo.mollweide().scale(100),
      "Natural Earth": d3.geo.naturalEarth().scale(100),
      "Nell-Hammer": d3.geo.nellHammer().scale(120),
      "Quartic Authalic": d3.geo.hammer().coefficient(Infinity).scale(95),
      "Robinson": d3.geo.robinson().scale(90),
      "Sinusoidal": d3.geo.sinusoidal().scale(90),
      "van der Grinten (I)": d3.geo.vanDerGrinten().scale(50),
      "Wagner VI": d3.geo.wagner6().scale(90),
      "Wagner VII": d3.geo.wagner7().scale(90),
      "Winkel Tripel": d3.geo.winkel3().scale(90),
      "Wiechel": d3.geo.wiechel().scale(90)
    };
    
    
    
    function generateRect(num, x, y, width, height) {
      var points = []
      var sideNum = Math.floor(num/4) + 1;
      // top
      d3.range(sideNum).forEach(function(i) {
        points.push({ x: x + i * width/sideNum, y: y })
      })
      // right
      d3.range(sideNum).forEach(function(i) {
        points.push({ x: x + width, y: y + i * height/sideNum })
      })
      // bottom
      d3.range(sideNum).forEach(function(i) {
        points.push({ x: x + width - i * width/sideNum, y: y + height })
      })
      // left
      d3.range(sideNum).forEach(function(i) {
        points.push({ x: x, y: y + height - i * height/sideNum })
      })
      return points;
    }
    
  </script>
</body>


<script>
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
  ga('create', 'UA-67666917-1', 'auto');
  ga('send', 'pageview');
</script>