d3 地理投影从正交过渡到 X
d3 geo projection transitions from orthographic to X
我正在处理一个显示不同地图投影的教育地图项目。我想在选择不同的投影之间实现变形过渡。
我找到了一个很好的例子来实现它,而且我没有太多的麻烦来重新创建它。不幸的是,我还需要能够裁剪投影。这对目标状态完美无缺,但在变形投影时却不行。
选择“正码”作为第一个投影,例如“ equiretangular”作为第二个:
https://bl.ocks.org/alexmacy/082cb12c8f4d5c0d5c4445c16a3db383
裁剪路径沿着较暗的线而不是当前地图范围。有没有正确实现的方法?
实现起来比看起来要复杂得多,我记得几年前看过这个。最干净的解决方案是创建一个新的预裁剪函数,该函数确定投影地球的哪些部分应该 behind/covered 靠近原点的部分。但事实证明,这相对难以定义——至少我是这样——而且也很难在新的预裁剪函数中使用。
相反,我们可以作弊。有几种方法,我会提出一种几乎可以解决问题的方法——尽管您仍然可以看到一些重叠。我们将使用 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
值的 'catch-all',反过来也很有用。
我正在处理一个显示不同地图投影的教育地图项目。我想在选择不同的投影之间实现变形过渡。
我找到了一个很好的例子来实现它,而且我没有太多的麻烦来重新创建它。不幸的是,我还需要能够裁剪投影。这对目标状态完美无缺,但在变形投影时却不行。
选择“正码”作为第一个投影,例如“ equiretangular”作为第二个: https://bl.ocks.org/alexmacy/082cb12c8f4d5c0d5c4445c16a3db383
裁剪路径沿着较暗的线而不是当前地图范围。有没有正确实现的方法?
实现起来比看起来要复杂得多,我记得几年前看过这个。最干净的解决方案是创建一个新的预裁剪函数,该函数确定投影地球的哪些部分应该 behind/covered 靠近原点的部分。但事实证明,这相对难以定义——至少我是这样——而且也很难在新的预裁剪函数中使用。
相反,我们可以作弊。有几种方法,我会提出一种几乎可以解决问题的方法——尽管您仍然可以看到一些重叠。我们将使用 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
值的 'catch-all',反过来也很有用。