问题描述
我正在开发一个教育地图项目,其中显示了不同的地图投影。我想在选择不同的投影之间实现变形过渡。
我找到了一个很好的示例来实现它,并且重新创建它并没有遇到太多麻烦。不幸的是,我还需要能够裁剪投影。这对目标状态完美无缺,但在变形投影时则不然。
您可以在此示例中在选择“正交”作为第一个投影时看到它,例如“Equirectangular”为第二个: https://bl.ocks.org/alexmacy/082cb12c8f4d5c0d5c4445c16a3db383
剪切路径遵循较暗的线条而不是当前地图范围。有没有办法正确实现它?
解决方法
这比看起来要复杂得多,我记得几年前看过这个。最干净的解决方案是创建一个新的预剪裁函数,该函数确定投影地球的哪些部分应该在靠近原点的部分后面/覆盖。但事实证明,这是相对难以定义的——至少我是这样——而且也很难在新的预剪裁功能中使用。
相反,我们可以作弊。有几种方法,我将提出一种几乎可以解决问题的方法 - 尽管如此,您仍然可以看到一些重叠。我们将使用 d3 的反子午线预剪裁来确保没有任何特征延伸到反子午线,然后我们将使用剪裁角度来移除需要移除的地球部分。
设置剪辑角度
当混合投影纯粹正交时,剪切角很大:剪切角在所有方向上都相同。这里应该是 90 度。
当等距柱状体在混合投影中主导时,不需要剪切角(我使用 180 度角,它不会剪切下面的任何东西)。这是因为整个地球应该仍然可见。
但除此之外,混合夹角在所有方向上都不相同——这就是为什么这不是一个完美的解决方案。然而,它确实消除了几乎所有的重叠。因此,当我们从主要是等距柱状投影到完全正交投影时,我们会慢慢减小夹角。
示例
从等距柱状投影开始并过渡到正交投影,只有在过渡完成 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。
,很好的答案安德鲁·里德!我只是做了一个小小的改变。我删除了 t > 0.4
if 语句并使用此剪辑转换为正投影:
clip = Math.PI/2 + (1 - t) * Math.PI/2
.. 以及用于从正视投影过渡的这段视频:
clip = Math.PI/2 + t * Math.PI/2
我喜欢这个,因为它稍微干净一点,是任何 t
值的“包罗万象”,反过来也很有用。