d3 的 fitSize 被新西兰 geojson 扭曲了
d3's fitSize distorted by New Zealand geojson
我正在尝试扩展此示例以包括亚洲和大洋洲(特别是澳大利亚和新西兰):https://bl.ocks.org/aholachek/700f930820f2704a957c070173327789
所以我用 https://geojson-maps.ash.ms/ 中的亚洲 + 大洋洲替换了示例中的 json 数据,并删除了所有太平洋小岛。当我这样做时,我看到整个地图向右倾斜(如下所示)。而通过简单地移除新西兰,地图将适当地缩放到整个 canvas.
代码的相关区域似乎是...
const projection = d3.geoMercator()
// d3's 'fitSize' magically sizes and positions the map for you
.fitSize([width, height], data);
但我找不到任何文档来解释为什么 fitSize 可能会在新西兰被绊倒(我需要将新西兰包括在最终的可视化中)。
抱歉 - 我解决了这个问题 - 新西兰地理数据中有一些小岛(在我的笔记本电脑屏幕上是看不见的)映射到图像的左侧。结束
我正在添加一个答案,因为可能无法删除要素以按预期对齐地图 - 例如白令海或环太平洋的地图。或者,您可能想包括那些新西兰小岛。
默认情况下,大多数 D3 投影以 0°N、0°E 为中心,逆子午线在 180°W/E。这意味着被反子午线分割的任何特征都可能最终出现在地图的两侧。 FitSize/fitExtent 然后将缩放和平移地图,以便地图的两边都可见,中间可能有一个大的空白 space。如您所述,您的功能连接了反子午线,因此 fitSize/fitExtent 无法按需工作。
projection.fitSize
和projection.fitExtent
都是设置投影的便捷方法projection.scale
和projection.translate
。缩放和平移都修改投影坐标——它们所能做的就是平移和缩放投影数据。因此 fitSize 和 fitExtent,也不是 translate 或 center,修改 antimeridian。
还有另外两种有用的投影方法:projection.center()
和projection.rotate()
。 Projection.center 以地理坐标转换地图。 .center() 指定的地理坐标和 .translate() 指定的像素坐标将在地图中对齐。但是,projection.rotate()
将在投影之前对地图应用旋转,这将移动反子午线。
projection.rotate
采用具有两个(或三个值)的数组,第一个代表经度,第二个代表纬度。通常,您希望将纬度保留为 0 - 否则这将改变墨卡托投影的外观。更改经度不会改变墨卡托(经度和投影的 x 值具有线性关系)。在您的情况下,将世界旋转 180 度将使本初子午线成为反子午线,这将确保投影的反子午线不会与您的特征相交,这意味着 fitSize 和 fitExtent 将根据需要工作,而您的特征将不会在地图的两个远边分开:
var width = 480;
var height = 480;
// feature crossing anti-meridian (180W/E), ensure proper winding direction.
var data = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
150.8203125,
-28.92163128242129
],
[
-121.0625,
-30.145127183376115
],
[
-121.765625,
-60.586967342258674
],
[
145.8984375,
-57.70414723434192
],
[
150.8203125,
-28.92163128242129
]
]
]
}
}
]
}
var svg = d3.select("svg");
var g = svg.append("g");
var projection = d3.geoMercator().rotate([180,0]);
var path = d3.geoPath(projection);
d3.json("https://d3js.org/world-110m.v1.json").then(function(world) {
// Draw the world.
let countries = topojson.feature(world, world.objects.countries).features;
projection.fitSize([width,height],data)
let features = g.selectAll("path")
.data(countries)
.enter()
.append("path")
.attr("d", path)
.style("stroke-width",1);
g.append("path")
.datum(data)
.attr("d",path)
.attr("fill","none")
.attr("stroke","black")
.attr("stroke-width",1);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>
<svg width="480" height="480"></svg>
我正在尝试扩展此示例以包括亚洲和大洋洲(特别是澳大利亚和新西兰):https://bl.ocks.org/aholachek/700f930820f2704a957c070173327789
所以我用 https://geojson-maps.ash.ms/ 中的亚洲 + 大洋洲替换了示例中的 json 数据,并删除了所有太平洋小岛。当我这样做时,我看到整个地图向右倾斜(如下所示)。而通过简单地移除新西兰,地图将适当地缩放到整个 canvas.
代码的相关区域似乎是...
const projection = d3.geoMercator()
// d3's 'fitSize' magically sizes and positions the map for you
.fitSize([width, height], data);
但我找不到任何文档来解释为什么 fitSize 可能会在新西兰被绊倒(我需要将新西兰包括在最终的可视化中)。
抱歉 - 我解决了这个问题 - 新西兰地理数据中有一些小岛(在我的笔记本电脑屏幕上是看不见的)映射到图像的左侧。结束
我正在添加一个答案,因为可能无法删除要素以按预期对齐地图 - 例如白令海或环太平洋的地图。或者,您可能想包括那些新西兰小岛。
默认情况下,大多数 D3 投影以 0°N、0°E 为中心,逆子午线在 180°W/E。这意味着被反子午线分割的任何特征都可能最终出现在地图的两侧。 FitSize/fitExtent 然后将缩放和平移地图,以便地图的两边都可见,中间可能有一个大的空白 space。如您所述,您的功能连接了反子午线,因此 fitSize/fitExtent 无法按需工作。
projection.fitSize
和projection.fitExtent
都是设置投影的便捷方法projection.scale
和projection.translate
。缩放和平移都修改投影坐标——它们所能做的就是平移和缩放投影数据。因此 fitSize 和 fitExtent,也不是 translate 或 center,修改 antimeridian。
还有另外两种有用的投影方法:projection.center()
和projection.rotate()
。 Projection.center 以地理坐标转换地图。 .center() 指定的地理坐标和 .translate() 指定的像素坐标将在地图中对齐。但是,projection.rotate()
将在投影之前对地图应用旋转,这将移动反子午线。
projection.rotate
采用具有两个(或三个值)的数组,第一个代表经度,第二个代表纬度。通常,您希望将纬度保留为 0 - 否则这将改变墨卡托投影的外观。更改经度不会改变墨卡托(经度和投影的 x 值具有线性关系)。在您的情况下,将世界旋转 180 度将使本初子午线成为反子午线,这将确保投影的反子午线不会与您的特征相交,这意味着 fitSize 和 fitExtent 将根据需要工作,而您的特征将不会在地图的两个远边分开:
var width = 480;
var height = 480;
// feature crossing anti-meridian (180W/E), ensure proper winding direction.
var data = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
150.8203125,
-28.92163128242129
],
[
-121.0625,
-30.145127183376115
],
[
-121.765625,
-60.586967342258674
],
[
145.8984375,
-57.70414723434192
],
[
150.8203125,
-28.92163128242129
]
]
]
}
}
]
}
var svg = d3.select("svg");
var g = svg.append("g");
var projection = d3.geoMercator().rotate([180,0]);
var path = d3.geoPath(projection);
d3.json("https://d3js.org/world-110m.v1.json").then(function(world) {
// Draw the world.
let countries = topojson.feature(world, world.objects.countries).features;
projection.fitSize([width,height],data)
let features = g.selectAll("path")
.data(countries)
.enter()
.append("path")
.attr("d", path)
.style("stroke-width",1);
g.append("path")
.datum(data)
.attr("d",path)
.attr("fill","none")
.attr("stroke","black")
.attr("stroke-width",1);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>
<svg width="480" height="480"></svg>