D3.js 给定geoJSON对象,将地图居中放置在d3中

D3.js 给定geoJSON对象,将地图居中放置在d3中,d3.js,geojson,topojson,D3.js,Geojson,Topojson,目前在d3中,如果你要绘制一个geoJSON对象,你必须缩放并转换它,以使其达到你想要的大小,并转换它以使其居中。这是一项非常繁琐的反复试验的任务,我想知道是否有人知道获得这些值的更好方法 例如,如果我有这个代码 var path, vis, xy; xy = d3.geo.mercator().scale(8500).translate([0, -1200]); path = d3.geo.path().projection(xy); vis = d3.select("#vis").app

目前在d3中,如果你要绘制一个geoJSON对象,你必须缩放并转换它,以使其达到你想要的大小,并转换它以使其居中。这是一项非常繁琐的反复试验的任务,我想知道是否有人知道获得这些值的更好方法

例如,如果我有这个代码

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");
});
我他妈的怎么才能不经过一点一点地获得.scale(8500)和.translate([0,-1200])呢?

有一种方法可以接受lat/lon对


据我所知,translate()只用于移动贴图的像素。我不知道如何确定比例。

要平移/缩放地图,您应该查看将SVG覆盖在传单上。这将比转换SVG容易得多。请参见此示例,然后

以下内容似乎可以大致满足您的要求。缩放似乎还可以。将其应用于我的地图时,有一个小偏移。这个小偏移可能是因为我使用了“平移”命令使地图居中,而我可能应该使用“居中”命令

  • 创建投影和d3.geo.path
  • 计算当前投影的边界
  • 使用这些边界计算比例和平移
  • 重新创建投影
  • 代码:

      var width  = 300;
      var height = 400;
    
      var vis = d3.select("#vis").append("svg")
          .attr("width", width).attr("height", height)
    
      d3.json("nld.json", function(json) {
          // create a first guess for the projection
          var center = d3.geo.centroid(json)
          var scale  = 150;
          var offset = [width/2, height/2];
          var projection = d3.geo.mercator().scale(scale).center(center)
              .translate(offset);
    
          // create the path
          var path = d3.geo.path().projection(projection);
    
          // using the path determine the bounds of the current map and use 
          // these to determine better values for the scale and translation
          var bounds  = path.bounds(json);
          var hscale  = scale*width  / (bounds[1][0] - bounds[0][0]);
          var vscale  = scale*height / (bounds[1][1] - bounds[0][1]);
          var scale   = (hscale < vscale) ? hscale : vscale;
          var offset  = [width - (bounds[0][0] + bounds[1][0])/2,
                            height - (bounds[0][1] + bounds[1][1])/2];
    
          // new projection
          projection = d3.geo.mercator().center(center)
            .scale(scale).translate(offset);
          path = path.projection(projection);
    
          // add a rectangle to see the bound of the svg
          vis.append("rect").attr('width', width).attr('height', height)
            .style('stroke', 'black').style('fill', 'none');
    
          vis.selectAll("path").data(json.features).enter().append("path")
            .attr("d", path)
            .style("fill", "red")
            .style("stroke-width", "1")
            .style("stroke", "black")
        });
    
    var宽度=300;
    var高度=400;
    var-vis=d3.选择(“vis”).追加(“svg”)
    .attr(“宽度”,宽度).attr(“高度”,高度)
    d3.json(“nld.json”,函数(json){
    //为投影创建第一个猜测
    var center=d3.geo.centroid(json)
    var量表=150;
    变量偏移=[宽度/2,高度/2];
    var projection=d3.geo.mercator().scale(比例)。center(中心)
    .翻译(抵销);
    //创建路径
    var path=d3.geo.path().projection(projection);
    //使用路径确定当前贴图的边界并使用
    //这些参数用于确定比例尺和平移的更好值
    var-bounds=path.bounds(json);
    var hscale=标度*宽度/(界限[1][0]-界限[0][0]);
    var vscale=标度*高度/(界限[1][1]-界限[0][1]);
    变量标度=(hscale
    我的答案与扬·范德兰的答案相近,但你可以稍微简化一些,因为你不需要计算地理质心;您只需要边界框。而且,通过使用未缩放、未转换的单位投影,可以简化数学

    守则的重要部分如下:

    // Create a unit projection.
    var projection = d3.geo.albers()
        .scale(1)
        .translate([0, 0]);
    
    // Create a path generator.
    var path = d3.geo.path()
        .projection(projection);
    
    // Compute the bounds of a feature of interest, then derive scale & translate.
    var b = path.bounds(state),
        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];
    
    // Update the projection to use computed scale & translate.
    projection
        .scale(s)
        .translate(t);
    
    在单位投影中计算特征后,您可以通过比较边界框的纵横比(
    b[1][0]-b[0][0]
    b[1][1]-b[0][1]
    )与画布的纵横比(
    width
    height
    )来计算适当的比例。在本例中,我还将边界框缩放到画布的95%,而不是100%,因此边缘上有一点额外的空间用于笔划和周围特征或填充

    然后,您可以使用边界框的中心(
    (b[1][0]+b[0][0])/2和
    (b[1][1]+b[0][1])/2和画布的中心(
    宽度/2和
    高度/2)计算翻译。请注意,由于边界框位于单位投影的坐标中,因此它必须乘以比例(
    s

    例如:

    还有一个相关的问题,即如何在不调整投影的情况下缩放集合中的特定特征,即将投影与几何变换相结合以放大和缩小。这使用了与上述相同的原理,但数学上略有不同,因为几何变换(SVG“transform”属性)与地理投影相结合

    例如:


    我在互联网上四处寻找一种无需大惊小怪的方法来将我的地图居中,并从扬·范德兰和姆博斯托克的答案中获得了灵感。如果您使用svg容器,这里有一种使用jQuery的简单方法。我为填充/边框等创建了95%的边框

    var width = $("#container").width() * 0.95,
        height = $("#container").width() * 0.95 / 1.9 //using height() doesn't work since there's nothing inside
    
    var projection = d3.geo.mercator().translate([width / 2, height / 2]).scale(width);
    var path = d3.geo.path().projection(projection);
    
    var svg = d3.select("#container").append("svg").attr("width", width).attr("height", height);
    

    如果你想要精确的缩放,这个答案对你来说是行不通的。但如果像我一样,您希望显示集中在容器中的地图,这就足够了。我试图显示墨卡托地图,发现这种方法对集中我的地图很有用,而且我可以很容易地切断南极部分,因为我不需要它。

    我是d3新手-将尝试解释我是如何理解它的,但我不确定我是否一切都正确

    秘密在于知道一些方法将在地图空间(纬度、经度)上操作,而另一些方法将在笛卡尔空间(屏幕上的x、y)上操作。地图空间(我们的星球)是(几乎)球形的,笛卡尔空间(屏幕)是平面的——为了将一个空间映射到另一个空间,你需要一个算法,称为。这个空间太短,无法深入探讨投影这一迷人的主题,以及它们如何扭曲地理特征,从而将球面变成平面;以此类推——总是有一个折衷方案(迈克·博斯托克有一个折衷方案)

    在d3中,投影对象有一个中心属性/设置器,以地图单位表示:

    b = d3.geo.bounds(feature);
    
    投影中心([
    var b = path.bounds(feature),
        s = 0.9 / Math.max(
                       (b[1][0] - b[0][0]) / width, 
                       (b[1][1] - b[0][1]) / height
                   );
    projection.scale(s); 
    
    b = d3.geo.bounds(feature);
    
    projection.center([(b[1][0]+b[0][0])/2, (b[1][1]+b[0][1])/2]);
    
    projection.translate([width/2, height/2]);
    
    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]);
    
    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);
    }
    
      var width  = 300;
      var height = 400;
    
      var vis = d3.select("#vis").append("svg")
          .attr("width", width).attr("height", height)
    
      d3.json("nld.json", function(json) {
          // create a first guess for the projection
          var center = d3.geo.centroid(json)
          var scale  = 150;
          var offset = [width/2, height/2];
          var projection = d3.geo.mercator().scale(scale).center(center)
              .translate(offset);
    
          // create the path
          var path = d3.geo.path().projection(projection);
    
          // using the path determine the bounds of the current map and use 
          // these to determine better values for the scale and translation
          var bounds  = path.bounds(json);
          var hscale  = scale*width  / (bounds[1][0] - bounds[0][0]);
          var vscale  = scale*height / (bounds[1][1] - bounds[0][1]);
          var scale   = (hscale < vscale) ? hscale : vscale;
          var offset  = [width - (bounds[0][0] + bounds[1][0])/2,
                            height - (bounds[0][1] + bounds[1][1])/2];
    
          // new projection
          projection = d3.geo.mercator().center(center)
            .scale(scale).translate(offset);
          path = path.projection(projection);
    
          // adjust projection
          var bounds  = path.bounds(json);
          offset[0] = offset[0] + (width - bounds[1][0] - bounds[0][0]) / 2;
          offset[1] = offset[1] + (height - bounds[1][1] - bounds[0][1]) / 2;
    
          projection = d3.geo.mercator().center(center)
            .scale(scale).translate(offset);
          path = path.projection(projection);
    
          // add a rectangle to see the bound of the svg
          vis.append("rect").attr('width', width).attr('height', height)
            .style('stroke', 'black').style('fill', 'none');
    
          vis.selectAll("path").data(json.features).enter().append("path")
            .attr("d", path)
            .style("fill", "red")
            .style("stroke-width", "1")
            .style("stroke", "black")
        });
    
          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)
    
    var projection = d3.geoMercator().fitSize([width, height], geojson);
    var path = d3.geoPath().projection(projection);
    
    g.selectAll('path')
      .data(geojson.features)
      .enter()
      .append('path')
      .attr('d', path)
      .style("fill", "red")
      .style("stroke-width", "1")
      .style("stroke", "black");