d3.js - 如何缩放分层 topojson map 中的所有项目并使其居中以适应 D3 版本 4?

标签 d3.js topojson

我已阅读并使用了 Mike Bostock 的 answer to Center a map in D3 given a geoJSON object ,这是一种根据 D3 版本 3 中的边界进行缩放以适合 geojson 文件中的一项的通用方法。我还 adapted it to use every item in a GeoJson 。该算法的关键部分是:

// Calculate bounding box transforms for entire collection
var b = path.bounds( geojson ),
s = .95 / Math.max((b[1][0] - b[0][0]) / w, (b[1][1] - b[0][1]) / h),
t = [(w - s * (b[1][0] + b[0][0])) / 2, (h - s * (b[1][1] + b[0][1])) / 2];

// Update the projection, which initially had .translate([0, 0]) .scale(1); 
projection
  .scale(s)
  .translate(t);

Using the V4 D3-geo docs我粗略地对其进行了调整,以使其与新的/修订的 d3.geoPath() 一起使用。 D3 4.0 版中的函数/对象。它似乎在下面的演示中工作(我正在使用 d3.geoPath().bounds() ,尚未尝试 V4 看似新的 d3.geoBounds() )。然而,我随后陷入了对其进行调整以获得跨越使用 topojson.js 转换的多层 topojson 文件中的所有层的边界框(即多个 GeoJSON 要素集合的边界框) 。 d3.geoPath().bounds()似乎只接受一个“层”和 d3.geoBounds()显得更加受限;到一项功能。

我也有点担心性能 - 所有这些似乎都涉及在可能很多层中循环可能非常多的形状,我觉得 D3 V4 中可能有更有效的方法?


这是一个粗略的演示,作为一个非常小的简单 TopoJSON 文件的起点,该文件应显示两个岛屿,每个岛屿都有几个子区域(威尔士和北爱尔兰的非常非常简化的版本用于简单演示,每个都位于单独的层上)。经过一番尝试和错误后,我成功地使其缩放并将 map 平移到“类似威尔士”的岛屿周围,但我不知道如何使其在所有图层上居中和缩放TopoJSON。

分层 TopoJSON 的标准方法似乎是将每个层转换为本质上独立的 GeoJSON,那么如何获取跨多个 GeoJSON 的边界,同时仍然能够将它们作为单独的层进行处理?

//Width and height
var w = 300;
var h = 200;

//Define map projection
var projection = d3.geoEquirectangular()
  .translate([0, 0])
  .scale(1);

//Define path generator
var path = d3.geoPath()
  .projection(projection);

//Create SVG element
var svg = d3.select("body")
  .append("svg")
  .attr("width", w)
  .attr("height", h);

//Load in GeoJSON data

var json = someUKJSON();

for (var key in json.objects) {
  if (json.objects.hasOwnProperty(key)) {
    
    // Topojson unpacks one layer at a time
    var layer = json.objects[key];
    var geojson = topojson.feature( json, layer );

    // Calculate bounding box transforms for entire collection
    var b = path.bounds(geojson),
      s = .95 / Math.max((b[1][0] - b[0][0]) / w, (b[1][1] - b[0][1]) / h),
      t = [(w - s * (b[1][0] + b[0][0])) / 2, (h - s * (b[1][1] + b[0][1])) / 2];

    // Update the projection    
    projection
      .scale(s)
      .translate(t);

    // Bind data and create one path per GeoJSON feature
    svg.selectAll("path")
      .data(geojson.features)
      .enter()
      .append("path")
      .attr("d", path)
      .style("fill", "steelblue");
    };
    // ...but each iteration will just zoom/centre on the latest
    // How do we expand the bounding box with each layer?
}

function someUKJSON(){
  return {"type":"Topology","transform":{"scale":[0.0034431267161520807,0.002017902170346754],"translate":[-7.8508544159644345,51.47680014500252]},
  "arcs":[[[1366,700],[-216,-155]],[[1150,545],[-121,275],[292,101],[45,-221]],[[1366,700],[23,-449]],[[1389,251],[-77,-96]],[[1312,155],[-75,-18]],[[1237,137],[-62,17]],[[1175,154],[-35,383]],[[1140,537],[10,8]],[[1175,154],[-71,-37]],[[1104,117],[-314,73],[350,347]],[[1237,137],[27,-119]],
  [[1264,18],[-35,-18]],[[1229,0],[-125,117]],[[1312,155],[28,-118]],[[1340,37],[-76,-19]],[[1385,12],[-45,25]],[[1389,251],[-4,-239]],[[1385,12],[-156,-12]],[[563,1572],[16,-7]],[[579,1565],[-55,-14]],[[524,1551],[39,21]],[[397,1870],[166,-298]],[[524,1551],[-63,-5]],[[461,1546],[-96,-18]],
  [[365,1528],[-109,10]],[[256,1538],[35,290]],[[291,1828],[106,42]],[[574,1342],[-124,192]],[[450,1534],[6,4],[5,8]],[[579,1565],[-5,-223]],[[365,1528],[85,6]],[[574,1342],[-219,-72],[-162,148]],[[193,1418],[63,120]],[[0,1515],[291,313]],[[193,1418],[-193,97]]],
  "objects":{"Wales":{"type":"GeometryCollection","geometries":[{"arcs":[[0,1]],"type":"Polygon","id":"Bedr"},{"arcs":[[2,3,4,5,6,7,-1]],"type":"Polygon","id":"Pong"},{"arcs":[[8,9,-7]],"type":"Polygon","id":"Hyda"},{"arcs":[[-6,10,11,12,-9]],"type":"Polygon","id":"Abwg"},
  {"arcs":[[13,14,-11,-5]],"type":"Polygon","id":"Cwaf"},{"arcs":[[15,-14,-4,16]],"type":"Polygon","id":"Anan"},{"arcs":[[17,-12,-15,-16]],"type":"Polygon","id":"Cave"}]},"nernIrel":{"type":"GeometryCollection","geometries":[{"arcs":[[18,19,20]],"type":"Polygon","id":"Blft"},
  {"arcs":[[21,-21,22,23,24,25,26]],"type":"Polygon","id":"nern"},{"arcs":[[27,28,-23,-20,29]],"type":"Polygon","id":"hern"},{"arcs":[[30,-28,31,32,-25]],"type":"Polygon","id":"sorn"},{"arcs":[[33,-26,-33,34]],"type":"Polygon","id":"wern"}]}}};
};
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>

最佳答案

这是一个非常简单的方法。这是我在这方面所做的最接近的尝试。把它留在这里,同时希望有人能想出更好的东西。

  • 创建一个一次性的类似 geojson 的假对象,纯粹用于计算边界
  • 将每个特征的特征数组合并到其中
  • 用它来计算边界并调整投影一次

这感觉很笨重,而且我找不到一种方法来一次绘制一层,我只能使用一次性的扁平化集合来绘制路径。

此外,奇怪的是 d3.merge() 似乎不适用于 geojson.features,这就是我使用 Array.concat( ) 来代替。不太明白那个,但这确实完成了。

//Width and height
var w = 300;
var h = 200;

//Define map projection
var projection = d3.geoEquirectangular()
  .translate([0, 0])
  .scale(1);

//Define path generator
var path = d3.geoPath()
  .projection(projection);

//Create SVG element
var svg = d3.select("body")
  .append("svg")
  .attr("width", w)
  .attr("height", h);

//Load in GeoJSON data

var json = someUKJSON();

//Create a fake bounds layer
var boundsCollection = {
  type: "FeatureCollection",
  features: []
}

for (var key in json.objects) {
  if (json.objects.hasOwnProperty(key)) {

    // Topojson unpacks one layer at a time
    var layer = json.objects[key];
    var geojson = topojson.feature(json, layer);

  boundsCollection.features = boundsCollection.features.concat( geojson.features );

  };
}


// Calculate bounding box transforms for entire collection
var b = path.bounds(boundsCollection),
  s = .95 / Math.max((b[1][0] - b[0][0]) / w, (b[1][1] - b[0][1]) / h),
  t = [(w - s * (b[1][0] + b[0][0])) / 2, (h - s * (b[1][1] + b[0][1])) / 2];

// Update the projection    
projection
  .scale(s)
  .translate(t);

    // Bind data and create one path per GeoJSON feature
    svg.selectAll("path")
      .data(boundsCollection.features)
      .enter()
      .append("path")
      .attr("d", path)
      .style("fill", "steelblue");


function someUKJSON() {
  return {
    "type": "Topology",
    "transform": {
      "scale": [0.0034431267161520807, 0.002017902170346754],
      "translate": [-7.8508544159644345, 51.47680014500252]
    },
    "arcs": [
      [
        [1366, 700],
        [-216, -155]
      ],
      [
        [1150, 545],
        [-121, 275],
        [292, 101],
        [45, -221]
      ],
      [
        [1366, 700],
        [23, -449]
      ],
      [
        [1389, 251],
        [-77, -96]
      ],
      [
        [1312, 155],
        [-75, -18]
      ],
      [
        [1237, 137],
        [-62, 17]
      ],
      [
        [1175, 154],
        [-35, 383]
      ],
      [
        [1140, 537],
        [10, 8]
      ],
      [
        [1175, 154],
        [-71, -37]
      ],
      [
        [1104, 117],
        [-314, 73],
        [350, 347]
      ],
      [
        [1237, 137],
        [27, -119]
      ],
      [
        [1264, 18],
        [-35, -18]
      ],
      [
        [1229, 0],
        [-125, 117]
      ],
      [
        [1312, 155],
        [28, -118]
      ],
      [
        [1340, 37],
        [-76, -19]
      ],
      [
        [1385, 12],
        [-45, 25]
      ],
      [
        [1389, 251],
        [-4, -239]
      ],
      [
        [1385, 12],
        [-156, -12]
      ],
      [
        [563, 1572],
        [16, -7]
      ],
      [
        [579, 1565],
        [-55, -14]
      ],
      [
        [524, 1551],
        [39, 21]
      ],
      [
        [397, 1870],
        [166, -298]
      ],
      [
        [524, 1551],
        [-63, -5]
      ],
      [
        [461, 1546],
        [-96, -18]
      ],
      [
        [365, 1528],
        [-109, 10]
      ],
      [
        [256, 1538],
        [35, 290]
      ],
      [
        [291, 1828],
        [106, 42]
      ],
      [
        [574, 1342],
        [-124, 192]
      ],
      [
        [450, 1534],
        [6, 4],
        [5, 8]
      ],
      [
        [579, 1565],
        [-5, -223]
      ],
      [
        [365, 1528],
        [85, 6]
      ],
      [
        [574, 1342],
        [-219, -72],
        [-162, 148]
      ],
      [
        [193, 1418],
        [63, 120]
      ],
      [
        [0, 1515],
        [291, 313]
      ],
      [
        [193, 1418],
        [-193, 97]
      ]
    ],
    "objects": {
      "Wales": {
        "type": "GeometryCollection",
        "geometries": [{
          "arcs": [
            [0, 1]
          ],
          "type": "Polygon",
          "id": "Bedr"
        }, {
          "arcs": [
            [2, 3, 4, 5, 6, 7, -1]
          ],
          "type": "Polygon",
          "id": "Pong"
        }, {
          "arcs": [
            [8, 9, -7]
          ],
          "type": "Polygon",
          "id": "Hyda"
        }, {
          "arcs": [
            [-6, 10, 11, 12, -9]
          ],
          "type": "Polygon",
          "id": "Abwg"
        }, {
          "arcs": [
            [13, 14, -11, -5]
          ],
          "type": "Polygon",
          "id": "Cwaf"
        }, {
          "arcs": [
            [15, -14, -4, 16]
          ],
          "type": "Polygon",
          "id": "Anan"
        }, {
          "arcs": [
            [17, -12, -15, -16]
          ],
          "type": "Polygon",
          "id": "Cave"
        }]
      },
      "nernIrel": {
        "type": "GeometryCollection",
        "geometries": [{
          "arcs": [
            [18, 19, 20]
          ],
          "type": "Polygon",
          "id": "Blft"
        }, {
          "arcs": [
            [21, -21, 22, 23, 24, 25, 26]
          ],
          "type": "Polygon",
          "id": "nern"
        }, {
          "arcs": [
            [27, 28, -23, -20, 29]
          ],
          "type": "Polygon",
          "id": "hern"
        }, {
          "arcs": [
            [30, -28, 31, 32, -25]
          ],
          "type": "Polygon",
          "id": "sorn"
        }, {
          "arcs": [
            [33, -26, -33, 34]
          ],
          "type": "Polygon",
          "id": "wern"
        }]
      }
    }
  };
};
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>

关于d3.js - 如何缩放分层 topojson map 中的所有项目并使其居中以适应 D3 版本 4?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39057114/

相关文章:

javascript - 如何绑定(bind)数据到对应的D3.js map 元素?

d3.js - D3 的美国邮政编码 TOPOJson

javascript - D3 : Setting selection transform attribute in a nicer way

SVG元素重叠问题

javascript - 尝试添加多个 D3 图

javascript - D3 : scale and color for choropleth map

d3.js - GeoJSON 和 TopoJSON 的区别

node.js - 如何运行 TopoJSON?

javascript - D3.js:添加名称基于数据的类

javascript - 如何使用 d3.js 将 svg 附加到预先存在的 svg?