如何在d3制图光栅重投影上修复地图边界?

Dan*_*mos 6 javascript raster map-projections d3.js d3.geo

我尝试使用此示例后的地图的光栅重投影.如果我kavrayskiy7通过Azimuthal Equidistant投影更改示例投影,

var projection = d3.geo.azimuthalEquidistant()
    .scale(90)
    .translate([width / 2, height / 2])
    .clipAngle(180 - 1e-3)
    .precision(.1);
Run Code Online (Sandbox Code Playgroud)

它应该将地球投射到光盘上(投影图的图像).然而,光栅重投影超出了该光盘并用扩展图像填充整个画布(反投影函数不是单射的,地图上的几个x/y点对应于单个lon/lat坐标).在原始示例中,应该使用该行避免这种情况

if (? > 180 || ? < -180 || ? > 90 || ? < -90) { i += 4; continue; }
Run Code Online (Sandbox Code Playgroud)

但是这个例子不起作用.我发现其他故障,例如使用Mollweide投影(两条线出现在两极)由于相同的效果.

要解决这个问题,一种方法是修正反向投影,使它们在x/y输入超出范围时返回错误或无.我的尝试是使用整个球体的前向投影来检查点是否在范围内,以获得具有地图边界的SVG路径,如下面的代码所示:

var path = d3.geo.path()
    .projection(projection);

var bdry = svg.append("defs").append("path")
    .datum({type: "Sphere"})
    .attr("id", "sphere")
    .attr("d", path);
Run Code Online (Sandbox Code Playgroud)

(例如参见这个例子).但是,我发现没有简单的方法来检查点[x,y]是否在SVG闭合路径内.

所以我的问题是:

  • 逆投影是否有错误,或者我没有正确使用它们?
  • 我怎么能找到一个[x,y]点在svg路径内,假设这是最好的方法?
  • 通过好奇心,d3 path函数的算法代码在哪里获取地图的边界轮廓?我在github回购中找不到它.

谢谢.

编辑:我在这个例子中经历了所有44个投影,我发现以下25个故障:

Albers,Bromley,Collignon,Eckert II,Eckert IV,Eckert VI,Hammer,Hill,Goode Homolosine,Lambert圆柱形等面积,Larrivée,Laskowski,McBryde-Thomas平极抛物线,McBryde-Thomas平极四分之一,McBryde- Thomas Flat-Polar Sinusoidal,Mollweide,Natural Earth,Nell-Hammer,Polyconic,Sinu-Mollweide,van der Grinten,van der Grinten IV,Wagner IV,Wagner VII,Winkel Tripel.

And*_*eid 2

我使用第二个答案只是因为这是解决同一问题的不同方法。同样,这个答案是一种替代方法,试图避免使用投影范围的 svg 轮廓的多边形解决方案中的点。

\n\n

这种替代方案应该(我只尝试过少数)适用于任何投影,而我的其他答案仅适用于投影到光盘的投影。其次,这种方法不会尝试定义投影区域来确定是否应该渲染像素,而是使用 d3.projection 本身。

\n\n
\n\n

由于多个点可以使用projection.invert返回相同的值,因此我们可以运行正向投影来验证是否应该绘制像素。

\n\n

如果projection(projection.invert(point)) == point那么该点位于我们投影的范围内。

\n\n

当然,其中可能存在一些精度/舍入误差,因此可以指定一定程度的容差。

\n\n

此检查适合 for 循环:

\n\n
for (var y = 0, i = -1; y < height; ++y) {\n    for (var x = 0; x < width; ++x) {\n\n        var p = projection.invert([x, y]), \xce\xbb = p[0], \xcf\x86 = p[1];\n\n        var pxy = projection(p);\n\n        var tolerance = 0.5;\n        if ( \xce\xbb > 180 || \xce\xbb < -180 || \xcf\x86 > 90 || \xcf\x86 < -90 ) { i += 4; continue; }\n        if ( (Math.abs(pxy[0] - x) < tolerance ) && (Math.abs(pxy[1] - y) < tolerance ) ) {\n\n            var q = ((90 - \xcf\x86) / 180 * dy | 0) * dx + ((180 + \xce\xbb) / 360 * dx | 0) << 2;\n            targetData[++i] = sourceData[q];\n            targetData[++i] = sourceData[++q];\n            targetData[++i] = sourceData[++q];\n            targetData[++i] = 255;\n\n        }\n        else {\n            i += 4;\n        } \n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

与其他答案一样,我在这里构建了一个块。

\n\n

我还没有检查这个答案的性能,并且需要这种检查似乎很奇怪,但它可能是您问题中提出的 svg 方法的合适替代方法。

\n