2
votes

I'm planning to have a geoJSON map inside my svg alongside other svg elements. I would like to be able to zoom (zoom+pan) in the map and keep the map in the same location with a bounding box. I can accomplish this by using a clipPath to keep the map within a rectangular area. The problem is that I also want to enable zooming and panning on my entire svg. If I do d3.select("svg").call(myzoom); this overrides any zoom I applied to my map. How can I apply zoom to both my entire svg and to my map? That is, I want to be able to zoom+pan on my map when my mouse is in the map's bounding box, and when the mouse is outside the bounding box, zoom+pan on the entire svg.

Here's example code: http://bl.ocks.org/nuernber/aeaac0e8edcf7ca93ade.

<svg id="svg" width="640" height="480" xmlns="http://www.w3.org/2000/svg" version="1.1">
  <defs>
    <clipPath id="rectClip">
      <rect x="150" y="25" width="400" height="400" style="stroke: gray; fill: none;"/>
    </clipPath>
  </defs>
  <g id="outer_group">
    <circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red" />
    <g id="svg_map" style="clip-path: url(#rectClip);">
    </g>
  </g>
</svg><br/>
<script type="text/javascript">
  var svg = d3.select("#svg_map");
  var mapGroup = svg.append("g");
  var projection = d3.geo.mercator();
  var path = d3.geo.path().projection(projection);

  var zoom = d3.behavior.zoom()
      .translate(projection.translate())
      .scale(projection.scale())
      .on("zoom", zoomed);
  mapGroup.call(zoom);

  var pan = d3.behavior.zoom()
      .on("zoom", panned);
  d3.select("svg").call(pan);

  mapGroup.attr("transform", "translate(200,0) scale(2,2)");
  d3.json("ne_110m_admin_0_countries/ne_110m_admin_0_countries.geojson", function(collection) {
      mapGroup.selectAll("path").data(collection.features)
        .enter().append("path")
        .attr("d", path)
        .attr("id", function(d) { return d.properties.name.replace(/\s+/g, "")})
        .style("fill", "gray").style("stroke", "white").style("stroke-width",1);
    }
  );

  function panned() {
    var x = d3.event.translate[0];
    var y = d3.event.translate[1];
    d3.select("#outer_group").attr("transform", "translate("+x+","+y+") scale(" + d3.event.scale + ")");
  }

  function zoomed() {
    previousScale = d3.event.scale;
    projection.translate(d3.event.translate).scale(d3.event.scale);
    translationOffset = d3.event.translate;
    mapGroup.selectAll("path").attr("d", path);
  }
</script>
1

1 Answers

1
votes

You need two zoom behaviours for that. The first one would be attached to the SVG and the second one to the map. In the zoom handlers you would have to take care of taking the appropriate action for each.