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.fitSizeprojection.fitExtent都是设置投影的便捷方法projection.scaleprojection.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>