Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D3: zoom to bounding box with d3-tiles

I have successfully layered a D3 (vector) map on top of a d3-tile (raster) map that pulls tiles from Mapbox. The manual zoom works perfectly, and both vector and raster are in sync.

I am now trying to implement the Mike Bostock 'zoom-to-bounding-box' feature, whereby the application zooms on a desired country upon user click. I think I am nearly there, but right now there seems to be a mismatch and the map zooms out into outer space, so to speak.

I have reproduced the issue in this jsfiddle.

What do I need to amend in the 'zoomed' function so that the map zooms correctly and as expected? I think this is where the issue lies:

  vector.selectAll("path")
    .attr("transform", "translate(" + [transform.x, transform.y] + ")scale(" + transform.k + ")")
    .style("stroke-width", 1 / transform.k);
like image 791
Noobster Avatar asked Jun 01 '18 07:06

Noobster


2 Answers

Edit: It looks like the main issue is just the update of projection

Using a constant projection

The main issue here is that you're changing the projection based on zooming and calculating the bounding box from them.

In zoomed, you have this line:

projection.scale...

It seems like you're trying to update the projection based on your zooming, but translating and scaling are already handled by the transform attribute of the svg elements.

The projection is instead used to translate spherical geographic coordinates into 2d space. It's used to calculate the bounds of a country and if it changes, the bounds move around, which is probably unintended. Removing this projection update gets you most of the way there.

The other big issue is that you clamp the scale in your click handler to values that are a lot lower than what you need (for instance you clamp the scale to a max of 8, but have an initial scale of 4096).

Removing the clamp and the projection update should get you click-to-zoom on countries. But we're not done yet!

Another issue is that your reset function does not reset to the original view, which makes it zoom off into the distance. Using the initial zoom settings instead of d3.zoomIdentity addresses the problem.

The results of these edits are here: https://jsfiddle.net/m7cn0p47/99/

Optional: Consolidating Transforms

It's generally inefficient to alter many DOM nodes. In this case you're altering each of your path vectors in zoomed. Applying the transform to one g element (in this case the g element with id vector) instead is usually more efficient.

Instead of:

vector.selectAll("path")
  .attr("transform", "translate(" + [transform.x, transform.y] + ")scale(" + transform.k + ")")
  .style("stroke-width", 1 / transform.k);

You can change it to simply:

vector
  .attr("transform", "translate(" + [transform.x, transform.y] + ")scale(" + transform.k + ")")
  .style("stroke-width", 1 / transform.k);

You'll also have to remove stroke-width from path in the CSS because it is overwriting it.

Here's an updated fiddle with those changes and corrections: https://jsfiddle.net/m7cn0p47/96/

like image 132
Steve Avatar answered Oct 21 '22 01:10

Steve


In your example you're applying a transformation directly to the parent SVG. In the bl.ocks example they created a group g inside the svg to apply the transformation to.

In your zoom function you're using css transformations as does the bl.ocks click zoom example. Updating to css transformation functions in the clickHandler and the same scale calculations as in the example, the click scaling works correctly.

https://jsfiddle.net/bamboo/m7cn0p47/3/

The scaling works better on states or more atomic sections. Places like USA, France Spain all have remote islands or Alaska. Which can give the effect of zooming out when its actually just including the surrounding areas.

like image 2
Lex Avatar answered Oct 21 '22 02:10

Lex