我已阅读并使用了 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/