title: d3.js zoom transition with map tiles
body: <p>I'm trying to implement a zoom transition on a map that uses tiles. I want to zoom in on one location, then go back and forth between two other locations. </p>
  
  <p>This is the example I'm working from: <a href="http://bl.ocks.org/mbostock/6242308" rel="nofollow">http://bl.ocks.org/mbostock/6242308</a>
  That does what I want, only I wish to use map tiles instead of a topojson or geojson file.</p>
  
  <p>Right now the map is calling the final location in the jump function, but the transitions between locations aren't working. Any idea on what's happening would be greatly welcomed-- nothing showing in console and I'm a bit stuck. Many thanks.</p>
  
  <p>My code is below, and it's also in this <a href="http://plnkr.co/edit/q3lUmsVgmL3yNXNGhw8T?p=preview" rel="nofollow">plunker</a>.</p>
  
  <pre><code>&lt;!DOCTYPE html&gt;
      &lt;meta charset="utf-8"&gt;
      &lt;style&gt;
  
      body {
        margin: 0;
      }
  
      #container {
        position: relative;
        overflow: hidden;
      }
  
      #map{
          width:100%;
          height:100%;
      }
  
      .layer {
        position: absolute;
      }
  
      .tile {
        pointer-events: none;
        position: absolute;
        width: 256px;
        height: 256px;
      }
  
      &lt;/style&gt;
      &lt;body&gt;
      &lt;script src="http://d3js.org/d3.v3.min.js"&gt;&lt;/script&gt;
      &lt;script src="http://d3js.org/d3.geo.tile.v0.min.js"&gt;&lt;/script&gt;
      &lt;div id="canvas"&gt;
          &lt;div id="container"&gt;
              &lt;g id="map"&gt;
                  &lt;div class="layer"&gt;&lt;/div&gt;
              &lt;/div&gt;
          &lt;/div&gt;
      &lt;/div&gt;
      &lt;script&gt;
  
      var width = Math.max(960, window.innerWidth),
          height = Math.max(500, window.innerHeight),
          prefix = prefixMatch(["webkit", "ms", "Moz", "O"]);
  
      var tile = d3.geo.tile()
          .size([width, height]);
  
      var sf = [-122.417, 37.775],
              belowsf = [-122.510962, 37.580284],
          ny = [-74.0064, 40.7142],
              brooklyn = [-73.975536, 40.691674];
  
      var projection = d3.geo.mercator()
              .scale(200000)
              .translate([-width / 2, -height / 2]); // just temporary
  
      var zoom = d3.behavior.zoom();
  
      var canvas = d3.select("#canvas")
              .style("width", width + "px")
              .style("height", height + "px");
  
      var container = d3.select("#container")
          .style("width", width + "px")
          .style("height", height + "px");
  
      canvas
  
                      .call(zoomTo(ny,200300).event)
                          .transition()
                          .duration(20000)
                          .each(jump);
  
      var map = d3.select("#map");
  
      var layer = d3.select(".layer");
  
              function zoomTo(location, newscale) {
                return zoom
                      .scale((newscale) * 2 * Math.PI)
                      .translate(projection(location).map(function(x) { return -x; }));
              }
  
              function jump() {
                var t = d3.select(this);
                (function repeat() {
                  t = t.transition()
                      .call(zoomTo(sf, 200000).event)
                    .transition()
                      .call(zoomTo(belowsf, 200000).event)
                      .each("end", repeat);
                })();
              }
  
      zoomed();
  
      function zoomed() {
        var tiles = tile
            .scale(zoom.scale())
            .translate(zoom.translate())
            ();
  
        projection
            .scale(zoom.scale() / 2 / Math.PI)
            .translate(zoom.translate());
  
        var image = layer
            .style(prefix + "transform", matrix3d(tiles.scale, tiles.translate))
          .selectAll(".tile")
            .data(tiles, function(d) { return d; });
  
        image.exit()
            .remove();
  
        image.enter().append("img")
            .attr("class", "tile")
            .attr("src", function(d) { return "http://" + ["a", "b", "c"][Math.random() * 3 | 0] + ".basemaps.cartocdn.com/light_all/" + d[2] + "/" + d[0] + "/" + d[1] + ".png"; })
            .style("left", function(d) { return (d[0] &lt;&lt; 8) + "px"; })
            .style("top", function(d) { return (d[1] &lt;&lt; 8) + "px"; });
      }
  
  
      function matrix3d(scale, translate) {
        var k = scale / 256, r = scale % 1 ? Number : Math.round;
        return "matrix3d(" + [k, 0, 0, 0, 0, k, 0, 0, 0, 0, k, 0, r(translate[0] * scale), r(translate[1] * scale), 0, 1 ] + ")";
      }
  
      function prefixMatch(p) {
        var i = -1, n = p.length, s = document.body.style;
        while (++i &lt; n) if (p[i] + "Transform" in s) return "-" + p[i].toLowerCase() + "-";
        return "";
      }
  
      function formatLocation(p, k) {
        var format = d3.format("." + Math.floor(Math.log(k) / 2 - 2) + "f");
        return (p[1] &lt; 0 ? format(-p[1]) + "°S" : format(p[1]) + "°N") + " "
             + (p[0] &lt; 0 ? format(-p[0]) + "°W" : format(p[0]) + "°E");
      }
  
      &lt;/script&gt;
      &lt;/body&gt;
      &lt;/html&gt;
  </code></pre>