首页 > 解决方案 > d3 的 fitSize 被新西兰 geojson 扭曲

问题描述

我正在尝试将此示例扩展到包括亚洲和大洋洲(特别是澳大利亚和新西兰):https ://bl.ocks.org/aholachek/700f930820f2704a957c070173327789

所以我用https://geojson-maps.ash.ms/中的 Asia + Oceania 替换了示例中的 json 数据,并修剪掉了所有的太平洋小岛。当我这样做时,我看到整个地图向右倾斜(如下所示)。而通过简单地删除新西兰,地图将适当地缩放到整个画布。

倾斜布局示例

代码的相关区域似乎是......

 const projection = d3.geoMercator()
   // d3's 'fitSize' magically sizes and positions the map for you
    .fitSize([width, height], data);

但是我找不到任何文档来解释为什么 fitSize 可能会在新西兰被绊倒(我需要将新西兰包含在最终的可视化中)。

标签: d3.jsgeojson

解决方案


我正在添加一个答案,因为可能无法删除要素以按预期对齐地图 - 例如白令海或环太平洋的地图。或者,您可能想要包括那些微小的新西兰岛屿。

默认情况下,大多数 D3 投影以 0°N、0°E 为中心,反子午线位于 180°W/E。这意味着被反子午线分割的任何特征都可能最终出现在地图的两侧。FitSize/fitExtent 然后将缩放和平移地图,以便地图的两侧都可见,中间可能有很大的空白空间。正如您所指出的,您的功能跨越了反子午线,因此 fitSize/fitExtent 不能按需要工作。

projection.fitSize和都是projection.fitExtent设置投影的便捷方法projection.scaleprojection.translate。缩放和平移都修改投影坐标 - 他们所能做的就是平移和缩放投影数据。因此 fitSize 和 fitExtent,也不平移或居中,修改反子午线。

还有另外两种有用的投影方法: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>


推荐阅读