实现起来比看起来要复杂得多,我记得几年前看过这个。最干净的解决方案是创建一个新的预剪裁函数,该函数确定投影地球的哪些部分应位于更靠近原点的部分后面/覆盖。但事实证明,这相对难以定义 - 至少我的我 - 也很难在新的预剪裁功能中使用。
相反,我们可以作弊。有几种方法,我会提出一种几乎可以解决问题的方法——不过你仍然可以看到一些重叠。我们将使用 d3 的逆子午线预剪裁来确保没有任何特征超出逆子午线,然后我们将使用剪裁角度来移除需要移除的地球部分。
设置剪辑角度
当混合投影纯正交时,剪裁角很大:剪裁角在所有方向上都相同。这里应该是 90 度。
当等距矩形在混合投影中占主导地位时,不需要剪裁角度(我使用 180 度的角度,不会剪裁下方的任何东西)。这是因为整个地球应该仍然可见。
但除此之外,混合夹角在所有方向上都不相同 - 这就是为什么这不是一个完美的解决方案。但是,它确实消除了几乎所有的重叠。因此,当我们从主要是等矩形投影到完全正交投影时,我们会慢慢减小剪辑角度。
示例
从 equirectangular 投影开始并过渡到正交投影,只有在过渡到 40% 时,我们才会开始将 clipAngle 从 180 度过渡到 90 度:
function getProjection(d) {
var clip = Math.PI; // Starting with 180 degrees: don't clip anything.
var projection = d3.geoProjection(project)
.rotate([posX, posY])
.fitExtent([[10, 10], [width - 10, height - 10]], {
type: "Sphere"
})
// Apply the two pre clipping functions:
.preclip( function(stream){
stream = d3.geoClipAntimeridian(stream) // cut antimeridian
return d3.geoClipCircle(clip)(stream) // apply clip angle
})
var path = d3.geoPath(projection);
function project(λ, φ) {
λ *= 180 / Math.PI,
φ *= 180 / Math.PI;
var p0 = projections[0]([λ, φ]),
p1 = projections[1]([λ, φ]);
// Don't actually clip anything until t == 0.4
if(t > 0.4) {
clip = Math.PI/2 + (0.60-(t-0.4)) * Math.PI/2
}
return [
(1 - t) * p0[0] + t * p1[0],
(1 - t) * -p0[1] + t * -p1[1]
];
}
return path(d)
}
这是example。