Question

I'm using d3.geo.tile() and have used it successfully before but this time the tile layer doesn't seem to draw at the same scale and translate as the point layer. The below code creates a map that pans and draws just fine, but draws the circles, which should be in the Mediterranean, in Africa. If I zoom in, it scales the tiles and circles just fine, it's as if my xy coordinates are off, but they aren't.

I get the feeling that it's actually drawing the base layer without offsetting and scaling it properly because it should be centering on the coordinates 12,42, but it's a great big mystery to me since this exact same code works fine in a different application.

If someone can spot some problem, or just a hint, that would help.

function createNewMap(){
 width = 1200, height = 800;
 var tile = d3.geo.tile()
  .size([1200, 800]);

var projection = d3.geo.mercator()
  .scale((1 << 12) / 2 / Math.PI)
  .translate([width / 2, height / 2]);

var center = projection([12, 42]);

var zoom = d3.behavior.zoom()
 .scale(projection.scale() * 2 * Math.PI)
 .scaleExtent([1 << 10, 1 << 17])
 .translate([width - center[0], height - center[1]])
 .on("zoom", zoomed);

projection
 .scale(1 / 2 / Math.PI)
 .translate([0, 0]);

var svg = d3.select("#newMapId").append("svg")
 .attr("width", width)
 .attr("height", height)
 .call(zoom);

 var raster = svg.append("g");
 var vector = svg.append("g");

 vector.selectAll("g").data(dataModule.polisData).enter().append("g")
  .attr("class", "sites")
  .attr("transform", function(d) {return "translate(" + (projection([d.xcoord,d.ycoord])[0]) + "," + (projection([d.xcoord,d.ycoord])[1]) + ")scale("+(projection.scale())+")"})
  .append("circle")
  .attr("class", "sitecirc");

  zoomed();

 function zoomed() {
  var tiles = tile
   .scale(zoom.scale())
   .translate(zoom.translate())
   ();

  var image = raster
   .attr("transform", "scale(" + tiles.scale + ")translate(" + tiles.translate + ")")
   .selectAll("image")
   .data(tiles, function(d) { return d; });

  image.exit()
   .remove();

  image.enter().append("image")
   .attr("xlink:href", function(d) { return "http://" + ["a", "b", "c", "d"][Math.random() * 4 | 0] + ".tiles.mapbox.com/v3/elijahmeeks.map-zm593ocx/" + d[2] + "/" + d[0] + "/" + d[1] + ".png"; })
   .attr("width", 1)
   .attr("height", 1)
   .attr("x", function(d) { return d[0]; })
   .attr("y", function(d) { return d[1]; });

  vector
   .attr("transform", "translate(" + zoom.translate() + ")scale(" + zoom.scale() + ")");

  d3.selectAll(".sitecirc")
   .attr("r", 10 / zoom.scale());

 }

Points are appended and projected correctly, base layer doesn

Was it helpful?

Solution

Your code appears to be based on my example that changes the SVG transform on zoom. Changing the transform is a nice technique when you have complex geometry that you just want to scale and translate when you pan or zoom — it’s typically faster than reprojecting everything — but it’s also more complex than changing the projection on zoom.

The code doesn’t change very much if you want to change the projection on zoom. In essence:

projection
    .scale(zoom.scale() / 2 / Math.PI)
    .translate(zoom.translate());

And then re-run your d3.geo.path to re-render. As shown in bl.ocks.org/9535021:

u.s. state capitals

Also, fixing the projection and changing the transform can cause precision problems if you zoom in a lot. Another reason to only use that technique when it offers substantial performance gains by avoid reprojection. And here reprojecting is super-cheap because it’s just a handful of points.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top