How to accurately zoom d3 maps which have already been translated

I have a map which has been translated to make it fit on the canvas properly.

I’m trying to implement a way to zoom it and it does work, but it moves away from center when you zoom in, rather than centering on the mouse or even the canvas.

  • How to remove decimal point from my y axis scale in d3js graph
  • Get multiple objects from stage by class name in KineticJS
  • Deserialize a JSON object in Fabric.js
  • How to add a touch event to the HTML5 Canvas color wheel?
  • looking for algorithm to find boundary of color region
  • Minimizing canvas “bitmap” data size
  • This is my code:

    function map(data, total_views) {
      var xy = d3.geo.mercator().scale(4350),
          path = d3.geo.path().projection(xy),
          transX = -320,
          transY = 648,
          init = true;
    
      var quantize = d3.scale.quantize()
        .domain([0, total_views*2/Object.keys(data).length])
        .range(d3.range(15).map(function(i) { return "map-colour-" + i; }));
    
      var map = d3.select("#map")
            .append("svg:g")
            .attr("id", "gb-regions")
            .attr("transform","translate("+transX+","+transY+")")
            .call(d3.behavior.zoom().on("zoom", redraw));
    
      d3.json(url_prefix + "map/regions.json", function(json) {
        d3.select("#regions")
            .selectAll("path")
                .data(json.features)
            .enter().append("svg:path")
                .attr("d", path)
                .attr("class", function(d) { return quantize(data[d.properties.fips]); });
      });
    
      function redraw() {
        var trans = d3.event.translate;
        var scale = d3.event.scale;
    
        if (init) {
          trans[0] += transX;
          trans[1] += transY;
          init = false;
        }
        console.log(trans);
    
        map.attr("transform", "translate(" + trans + ")" + " scale(" + scale + ")");
      }
    }
    

    I’ve found that adding the initial translation to the new translation (trans) works for the first zoom, but for all subsequent zooms it makes it worse. Any ideas?

  • Cross-Browser Canvas using nodeJS no signature found for the function
  • Generating triangles from a random set of points
  • A* Pathfinding in a hexagonal grid
  • Canvas tainted by cross-origin data
  • Is WebGL or Canvas the only way to get SVG Keyframe Animations Hardware Accelerated?
  • SVG to PNG retaining CSS using javascript
  • One Solution collect form web for “How to accurately zoom d3 maps which have already been translated”

    Here’s a comprehensive starting-point: semantic zooming of force directed graph in d3

    And this example helped me specifically (just rip out all the minimap stuff to make it simpler): http://codepen.io/billdwhite/pen/lCAdi?editors=001

     var zoomHandler = function(newScale) {
            if (!zoomEnabled) { return; }
            if (d3.event) {
                scale = d3.event.scale;
            } else {
                scale = newScale;
            }
            if (dragEnabled) {
                var tbound = -height * scale,
                    bbound = height  * scale,
                    lbound = -width  * scale,
                    rbound = width   * scale;
                // limit translation to thresholds
                translation = d3.event ? d3.event.translate : [0, 0];
                translation = [
                    Math.max(Math.min(translation[0], rbound), lbound),
                    Math.max(Math.min(translation[1], bbound), tbound)
                ];
            }
    
            d3.select(".panCanvas, .panCanvas .bg")
                .attr("transform", "translate(" + translation + ")" + " scale(" + scale + ")");
    
            minimap.scale(scale).render();
        }; // startoff zoomed in a bit to show pan/zoom rectangle
    

    Though I had to tweak that function a fair bit to get it working for my case, but the idea is there. Here’s part of mine. (E.range(min,max,value) just limits value to be within the min/max. The changes are mostly because I’m treating 0,0 as the center of the screen in this case.

    // limit translation to thresholds
                var offw = width/2*scale;
                var offh = height/2*scale;
                var sw = width*scale/2 - zoomPadding;
                var sh = height*scale/2- zoomPadding;
    
                translate = d3.event ? d3.event.translate : [0, 0];
                translate = [
                    E.range(-sw,(width+sw), translate[0]+offw),
                    E.range(-sh,(height+sh), translate[1]+offh)
                ];
            }
    
    
            var ts = [translate[0], translate[1]];
            var msvg  = [scale, 0, 0, scale, ts[0], ts[1]];