Center a map in d3 given a geoJSON object

2018-12-31 03:41发布

Currently in d3 if you have a geoJSON object that you are going to draw you have to scale it and translate it in order to get it to the size that one wants and translate it in order to center it. This is a very tedious task of trial and error, and I was wondering if anyone knew a better way to obtain these values?

So for instance if I have this code

var path, vis, xy;
xy = d3.geo.mercator().scale(8500).translate([0, -1200]);

path = d3.geo.path().projection(xy);

vis = d3.select("#vis").append("svg:svg").attr("width", 960).attr("height", 600);

d3.json("../../data/ireland2.geojson", function(json) {
  return vis.append("svg:g")
    .attr("class", "tracts")
    .selectAll("path")
    .data(json.features).enter()
    .append("svg:path")
    .attr("d", path)
    .attr("fill", "#85C3C0")
    .attr("stroke", "#222");
});

How the hell do I obtain .scale(8500) and .translate([0, -1200]) without going little by little?

11条回答
美炸的是我
2楼-- · 2018-12-31 03:51

To pan/zoom the map you should look at overlaying the SVG on Leaflet. That will be a lot easier than transforming the SVG. See this example http://bost.ocks.org/mike/leaflet/ and then How to change the map center in leaflet

查看更多
泛滥B
3楼-- · 2018-12-31 03:51

How I centered a Topojson, where I needed to pull out the feature:

      var projection = d3.geo.albersUsa();

      var path = d3.geo.path()
        .projection(projection);


      var tracts = topojson.feature(mapdata, mapdata.objects.tx_counties);

      projection
          .scale(1)
          .translate([0, 0]);

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

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

        svg.append("path")
            .datum(topojson.feature(mapdata, mapdata.objects.tx_counties))
            .attr("d", path)
查看更多
低头抚发
4楼-- · 2018-12-31 03:59

With d3 v4 its getting way easier!

var projection = d3.geoMercator().fitSize([width, height], geojson);
var path = d3.geoPath().projection(projection);

and finally

g.selectAll('path')
  .data(geojson.features)
  .enter()
  .append('path')
  .attr('d', path)
  .style("fill", "red")
  .style("stroke-width", "1")
  .style("stroke", "black");

Enjoy, Cheers

查看更多
一个人的天荒地老
5楼-- · 2018-12-31 04:03

There is a center() method you can use that accepts a lat/lon pair.

From what I understand, translate() is only used for literally moving the pixels of the map. I am not sure how to determine what scale is.

查看更多
怪性笑人.
6楼-- · 2018-12-31 04:06

In addition to Center a map in d3 given a geoJSON object, note that you may prefer fitExtent() over fitSize() if you want to specify a padding around the bounds of your object. fitSize() automatically sets this padding to 0.

查看更多
人间绝色
7楼-- · 2018-12-31 04:06

With mbostocks' answer, and Herb Caudill's comment, I started running into issues with Alaska since I was using a mercator projection. I should note that for my own purposes, I am trying to project and center US States. I found that I had to marry the two answers with Jan van der Laan answer with following exception for polygons that overlap hemispheres (polygons that end up with a absolute value for East - West that is greater than 1):

  1. set up a simple projection in mercator:

    projection = d3.geo.mercator().scale(1).translate([0,0]);

  2. create the path:

    path = d3.geo.path().projection(projection);

3.set up my bounds:

var bounds = path.bounds(topoJson),
  dx = Math.abs(bounds[1][0] - bounds[0][0]),
  dy = Math.abs(bounds[1][1] - bounds[0][1]),
  x = (bounds[1][0] + bounds[0][0]),
  y = (bounds[1][1] + bounds[0][1]);

4.Add exception for Alaska and states that overlap the hemispheres:

if(dx > 1){
var center = d3.geo.centroid(topojson.feature(json, json.objects[topoObj]));
scale = height / dy * 0.85;
console.log(scale);
projection = projection
    .scale(scale)
    .center(center)
    .translate([ width/2, height/2]);
}else{
scale = 0.85 / Math.max( dx / width, dy / height );
offset = [ (width - scale * x)/2 , (height - scale * y)/2];

// new projection
projection = projection                     
    .scale(scale)
    .translate(offset);
}

I hope this helps.

查看更多
登录 后发表回答