Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constraining map panning with zoom.translateExtent in D3 v4

I'm trying to display a map of a single state, with zooming and panning constrained to the boundaries of the state. It's mostly working, except for the panning constraint when the state path is scaled to fit a smaller container. I think this comes down to me not understanding what arguments to use for zoom.translateExtent (although I'm very new to this, so it could be something else).

Live example on bl.ocks.org, with links to prior art.

One notable thing is that I'm using a null projection for d3.geoPath, because I used ogr2ogr to generate a shapefile in projected coordinates for each state. That's why I used a zoom transform to fit the map to its container.

like image 325
bhrutledge Avatar asked Jul 25 '16 02:07

bhrutledge


2 Answers

@McGiogen's solution is almost correct but misses that MIN needs to vary depending on the zoom scale factor transform.k.

I drew a diagram to see how I needed to constrain my svg to always be contained inside the zoomed view (depicted in my drawing as the LARGER of the boxes, only a portion of which is visible to the user):

enter image description here

(since the constraint x+kw >= w is equivalent to x >= (1-k)w, with a similar argument for y)

thus assuming your svg container size [w, h]:

function zoomed() {
    var t = d3.event.transform;

    t.x = d3.min([t.x, 0]);
    t.y = d3.min([t.y, 0]);
    t.x = d3.max([t.x, (1-t.k) * w]);
    t.y = d3.max([t.y, (1-t.k) * h]);

    svg.attr("transform", t);
}
like image 191
Michael Currie Avatar answered Sep 22 '22 12:09

Michael Currie


I'm facing the same problem today and I've done some tests.

I've noticed that it's the same weird behaviour happening when you have a translateExtent box smaller than the content's elements.

In your (and mine) code the same behaviour is triggered by zooming out: it doesn't matter if you have the translateExtent box correctly set with no zoom, if you zoom out the box is reduced at higher rate than the elements and at some point you will have translateExtent box smaller than the content (and the weird behaviour).

I temporary solved this as said here D3 pan+ zoom constraints

var MIN = {x: 0, y: -500},     //top-left corner
    MAX = {x: 2000, y: 500};   //bottom-right corner

function zoomed() {
   var transform = d3.event.transform;

   // limiting tranformation by MIN and MAX bounds
   transform.x = d3.max([transform.x, MIN.x]);
   transform.y = d3.max([transform.y, MIN.y]);
   transform.x = d3.min([transform.x, MAX.x]);
   transform.y = d3.min([transform.y, MAX.y]);

   container.attr("transform", transform);
}

I'm still a d3 newbie but I think that this is a bug in translateExtent code.

like image 37
McGiogen Avatar answered Sep 22 '22 12:09

McGiogen