HGalan
HGalan

Reputation: 83

D3.v4 Projection Transition not Working

I've only recently started to code, and this is my first time posting, so apologies for any breaches of protocol ;-P

I'm trying to reproduce the Projection Transitions from this Mike Bostock bl.ock

http://bl.ocks.org/mbostock/3711652

but in d3.v4. I can get the transitions to execute, but without animation in- between. I think this is something to do with the way I'm executing the ProjectionTween function or perhaps the way Update selects the svg, but I can't seem to get it to work otherwise.

Any help would be greatly appreciated.

//Options, insert projections here

var options = [
  {name: "Mercator", projection: d3.geoMercator()},
  {name: "Hammer", projection: d3.geoHammer()},
  {name: "ConicEqual", projection: d3.geoConicEqualArea()},
  {name: "ConicEqui", projection: d3.geoConicEquidistant()},
  {name: "ConicConformal", projection: d3.geoConicConformal()},
  {name: "Ginzburg8", projection: d3.geoGinzburg8()},
  {name: "Laskowski", projection: d3.geoLaskowski()},
   {name: "Times", projection: d3.geoTimes()},
  {name: "LaGrange", projection: d3.geoLagrange()}
  ];

options.forEach(function(o) {
  o.projection.rotate([0, 0]).center([0, 0]);
});

var interval = setInterval(loop, 1500),
    i = 0,
    n = options.length - 1;



var c = document.getElementById('container');

var width = c.offsetWidth;

var height = width / 2;

var div = d3.select("body").append("div") 
    .attr("class", "tooltip")       
    .style("opacity", 0);  

var graticule = d3.geoGraticule();


 
var menu = d3.select("#projection-menu")
    .on("change", change);

menu.selectAll("option")
    .data(options)
  .enter().append("option")
    .text(function(d) { return d.name; });



setup(width,height);

function setup(width,height){
    projection = options[i].projection // N.B. geoPeirceQuincuncial in 1.1+
    .translate([(width/2), (height/2)])
    .scale( width / 2 / Math.PI);

  //path = d3.geo.path().projection(projection);
  path = d3.geoPath().projection(projection);

  svg = d3.select("#container").append("svg")
      .attr("width", width)
      .attr("height", height)
      .append("g");

  g = svg.append("g");


g.append("path")
    .datum(graticule)
    .attr("class", "graticule")
    .attr("d", path); 

g.append("path")
    .datum({type: "Sphere"})
    .attr("class", "sphere")
    .attr("d", path)
    .attr("fill", "#f1f1f1")
    .attr("stroke", "black")
     .attr("opacity", 0.3);


}


d3.queue()
    .defer(d3.json, "world-50m.v1.json")
    .await(ready);

function ready(error, world) {
 
  var countries = topojson.feature(world, world.objects.countries).features;
  topo = countries;
  draw(topo);
}


function draw(topo) {


  var country = g.selectAll(".country").data(topo);

  country.enter().insert("path")
      .attr("class", "country")
      .attr("d", path)
      .style("fill", "black");
 }


//Loop / interval / option / update

function loop() {
  var j = Math.floor(Math.random() * n);
  menu.property("selectedIndex", i = j + (j >= i));
  update(options[i]);
}

function change() {
  clearInterval(interval);
  update(options[this.selectedIndex]);
}

function update(option) {
  svg.selectAll("path").transition()
      .duration(750)
      .attrTween("d", projectionTween(projection, projection = option.projection));
}

function projectionTween(projection0, projection1) {
  return function(d) {
    var t = 0;

    projection = options[i].projection // N.B. geoPeirceQuincuncial in 1.1+
    .translate([(width/2), (height/2)])
    .scale( width / 2 / Math.PI);

  //path = d3.geo.path().projection(projection);
  path = d3.geoPath().projection(projection);

    function project(λ, φ) {
      λ *= 180 / Math.PI, φ *= 180 / Math.PI;
      var p0 = projection0([λ, φ]), p1 = projection1([λ, φ]);
      return [(1 - t) * p0[0] + t * p1[0], (1 - t) * -p0[1] + t * -p1[1]];
    }

    return function(_) {
      t = _;
      return path(d);
    };


  };
}
.country {
  fill: grey;
  stroke-width: 0.1px;
}

.graticule {
  fill: none;
  stroke: grey;
  stroke-width: 0.5px;
  stroke-opacity: 0.3;
}

#container {
  margin:10px 10%;
  border:2px solid #000;
  border-radius: 5px;
  height:100%;
  overflow:hidden;
  background: none;
}
<!DOCTYPE html>
<head>
    <meta charset="utf-8">
    <select id="projection-menu"></select>
</head>
<body>
    <div id="container"></div>
    <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.v2.min.js"></script>
    <script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
</body>
</html>

Playground

Upvotes: 4

Views: 738

Answers (1)

HGalan
HGalan

Reputation: 83

I figured it out! I was misunderstanding how the projectionTween function was working. I basically forgot to transition it from v3 to v4. so the code:

var projection = d3.geo.projection(project) .scale(1) .translate([width / 2, height / 2]);

becomes:

var projection = d3.geoProjection(project) .scale(1) .translate([width / 2, height / 2]);

Hope this helps someone!

Upvotes: 2

Related Questions