0

I am making a small multiples chart that visualizes multiple neighborhoods.

Imagine something like this, but with neighborhoods not a country:

iraq

My issue is, when I append each path in an enter() method, each neighborhood skews such that its position reflects its position in the city at large. I want each neighborhood centered.

This image displays my trouble (each neighborhood offset from the others):

nyc2

I can use Mike Bostock's instructions for centering a map, which are all well and good. But then I need to recenter every neighborhood. How can I do that such that I'm passing an instance of d into the data.bounds() method and redefining each projection?

Here's how the initial definition of centering happens:

(function run() { 
    d3.json("./data/output.geojson", function(error, map) {


    var b = path.bounds(map),
 s = .95 / Math.max((b[1][0] - b[0][0]) / width, (b[1][4] - b[0][5]) / height),
t = [(width - s * (b[1][0] + b[0][0])) / 2, (height - s * (b[1][6] + b[0][7])) / 2];

projection
    .scale(s)
    .translate(t);


  var chart = d3.select("#charts").selectAll("svg")
           .data(map.features)
           .enter()
           .append("svg")
               .attr("width", mapWidth )
               .attr("height", mapHeight )
               .append("g")

              chart
                .append("path")
                .attr("d", path)
                .style("fill","steelblue");

    });

})()

Will it be necessary to make each map projection its own instance of data entry with the .data().enter() pattern?

Edit:

Final version looks like this:

final

Community
  • 1
  • 1
Union find
  • 7,759
  • 13
  • 60
  • 111

1 Answers1

2

You can use .each():

chart.append("path")
  .each(function(d) {
    var b = path.bounds(d),
        s = 1.5 / Math.max((b[1][0] - b[0][0]) / mapWidth, (b[1][1] - b[0][1]) / mapHeight),
        t = [(mapWidth - s * (b[1][0] + b[0][0])) / 2, (mapHeight - s * (b[1][1] + b[0][1])) / 2];

    projection.scale(s).translate(t);
    d3.select(this).attr("d", path);
  });

This doesn't quite work because the bounds of a feature depend on the projection the path is using. By changing the projection, you're also changing the bounds of subsequent features. To fix that, reset the projection parameters to their previous values after setting the d attribute:

var os = projection.scale(),
    ot = projection.translate();

// ...

projection.scale(s).translate(t);
d3.select(this).attr("d", path);
projection.scale(os).translate(ot);
Lars Kotthoff
  • 107,425
  • 16
  • 204
  • 204