如何使 geoAlbersUSA 投影像 geoMercator 一样笔直(而不是弯曲)?

eby*_*byt 3 gis geojson map-projections d3.js

我按照指南在 d3js 中创建了美国的基本地图

指南链接 - http://bl.ocks.org/michellechandra/0b2ce4923dc9b5809922


我已经了解了 GeoJSON 和地图投影的基础知识。

目前我的地图看起来像这样 - (地图 1) - 这使用d3.geoAlbersUsa()投影 在此输入图像描述

我希望我的地图看起来像这样 - (地图 2) - 即顶部不是弯曲的,而是直线边界

链接https://www.worldatlas.com/webimage/countrys/namerica/usstates/usa50mer.htm

在此输入图像描述

要求 -

  1. 我想让阿拉斯加和夏威夷在地图中保持可见,就像两幅图像中一样

我已经尝试过什么 -

  1. 我尝试过使用d3.geoMercator()投影,但阿拉斯加太大,无法容纳在视口中,并且位于我不想要的左上角。

And*_*eid 6

你无法矫正阿尔伯斯。阿尔伯斯投影是圆锥投影 - 纬线的大小不同,因为它们被投影到圆锥上,这意味着由于投影长度不同(地球上的纬线长度也不同),因此会出现一条曲线。

圆柱形墨卡托投影将较短的纬线拉伸以占据与赤道相同的宽度,这就是为什么墨卡托投影中的纬线是直的、水平的且长度相等的原因。

我们可以滥用 Albers 投影来消除部分曲线,但 d3 的 geoAlbersUsa 投影是复合投影,不允许进行此类修改。它实际上只是预测的集合。

相反,我们可以使用 d3.geoTransform 和 d3.geoMercator 创建自己的复合投影。结果应该是这样的:

在此输入图像描述

这是一个简单的 d3 geoTransform:

  let projection = d3.geoTransform({
      point: function(x, y) {
          this.stream.point(x*2,y*2);
      }
    });
Run Code Online (Sandbox Code Playgroud)

这只是将 x 和 y 值乘以二,它可以传递给路径生成器,如下所示:d3.geoPath(projection);

要在这里进行复合投影,我们需要正确设置三个投影,然后正确翻译到地图上。我假设 960x500 地图构建了这些投影(类似于 d3.geoAlbersUsa):

  let scale = 800;
  let width = 960;
  let height = 500;

  let continental = d3.geoMercator()
    .center([-98.58,39.83])
    .translate([width*0.5,height*0.42])  // placed near center
    .scale(scale);

  let hawaii = d3.geoMercator()
    .center([-157.25,20.8])
    .scale(scale)
    .translate([width*0.35,height*0.87])  // placed near bottom, slightly left of center

  let alaska = d3.geoMercator()
    .center([-152.5,65])
    .translate([width*0.15,height*0.8])    // placed in lower left
    .scale(scale*0.3)
Run Code Online (Sandbox Code Playgroud)

当人们移动到极端纬度时,墨卡托确实夸大了大小,因此地理上已经很大的阿拉斯加(超过德克萨斯州大小的两倍)在投影时变得太大了。我已将其缩小以使其适合(这样它大约是投影尺寸的 1/10)。

我们还需要选择何时使用什么投影,因此根据一些规则,我们可以确定一个点是在阿拉斯加、夏威夷还是 48 以内:

  let projection = d3.geoTransform({
      point: function(x, y) {
        if(y < 50 && x < -140) {  // south and west : hawaii
          this.stream.point(...hawaii([x,y]));
        }
        else if (y > 50) {       // north : alaska
          this.stream.point(...alaska([x,y]));
        }
        else {                    // otherwise, it's in the lower 48.
          this.stream.point(...continental([x,y]));
        }
      }
    });
Run Code Online (Sandbox Code Playgroud)

就是这样,我们有了三个投影和一个方法来确定给定点应该使用哪个投影。

我将把它们全部捆绑起来,以d3.geoMercatorUsa()使其更加独立,我们得到:

  let projection = d3.geoTransform({
      point: function(x, y) {
          this.stream.point(x*2,y*2);
      }
    });
Run Code Online (Sandbox Code Playgroud)
  let scale = 800;
  let width = 960;
  let height = 500;

  let continental = d3.geoMercator()
    .center([-98.58,39.83])
    .translate([width*0.5,height*0.42])  // placed near center
    .scale(scale);

  let hawaii = d3.geoMercator()
    .center([-157.25,20.8])
    .scale(scale)
    .translate([width*0.35,height*0.87])  // placed near bottom, slightly left of center

  let alaska = d3.geoMercator()
    .center([-152.5,65])
    .translate([width*0.15,height*0.8])    // placed in lower left
    .scale(scale*0.3)
Run Code Online (Sandbox Code Playgroud)