我正在使用D3和D3.geo.projection创建一个包含数据点的旋转地球仪。
当我只是用圆圈绘制点时,一切正常(即点数"黯然失色"当他们在地平线后旋转时):
svg.append("g")
.attr("class","points")
.selectAll("text")
.data(places.features)
.enter()
//for circle-point------------------------------
.append("path")
.attr("d", path.pointRadius(function(d) {
if (d.properties)
return 3+(4*d.properties.scalerank);
}))
.attr("d", path)
.attr("class", "point")
.on("click",pointClick)
;
但现在我试图绘制符号而不是圆圈:
svg.append("g")
.attr("class","points")
.selectAll("text")
.data(places.features)
.enter()
//for image-------------------------------------
.append("image")
.attr("xlink:href", "img/x_symbol.png")
.attr("x", -12)
.attr("y", -12)
.attr("width", 24)
.attr("height", 24)
.attr("transform", function(d) {
return "translate(" + projection([
d.properties.longitude,
d.properties.latitude
]) + ")"
})
.attr("class", "point")
.on("click",pointClick)
;
虽然这是有效的,并且符号在地球上的正确位置绘制,但即使它们缠绕在地球的后面,它们仍然存在。 如果我有办法确定它们是否已经黯然失色,我可以用可见性属性隐藏它们,但是我没有在d3.geo.projection中看到一个方法来做到这一点。有什么想法吗?
答案 0 :(得分:3)
好的,所以我能够通过计算投影中心和相关点之间的大圆距离来至少模拟隐藏在地球背后的图像。如果距离大于π/ 2,则该点将超出地平线:
.attr("opacity", function(d) {
var geoangle = d3.geo.distance(
d.geometry.coordinates,
[
-projection.rotate()[0],
projection.rotate()[1]
]);
if (geoangle > 1.57079632679490)
{
return "0";
} else {
return "1.0";
}
})
我确信我可以通过淡出它们,因为它们接近边缘,禁用点击等等而变得更加迷人,但现在我可以继续......
答案 1 :(得分:2)
计算该点是否可见
如果您有投影,
const chartProjection = d3.geo.orthographic();
您可以将其转换为路径功能:
const path = d3.geo.path()
.projection(chartProjection);
然后,您可以评估每个点的可见性。对于投影后面的值,path
将返回undefined
。
function getVisibility(d) {
const visible = path(
{type: 'Point', coordinates: [d.longitude, d.latitude]});
return visible ? 'visible' : 'hidden';
}
// Update all text elements.
svg.selectAll('text')
.attr('visibility', getVisibility);
答案 2 :(得分:0)
我知道这是一个老问题,但想添加一个新的解决方案。 您可以使用projection.stream过滤,解析,然后以像素为单位返回投影的x,y。此方法还可以过滤掉当前(缩放)范围之外的点。
注意这将过滤(剪辑)当前投影下不可见的所有点。如果你真的想要保留所有点,添加另一个循环来比较两个数组。但是在我使用反应特性的情况下,这非常有效。
let newStream = []
let stream = youCurrentProjection.stream({
point: function (x, y) {
newStream.push([x, y])
}
})
arrayOfPoints.forEach(point => {
stream.point(point.x, point.y)
})
或构建一个闭包来传递索引
let streamWrapper = function (x, y, index) {
let stream = youCurrentProjection.stream({
point: function (x, y) {
arrayOfPoints[index].isVisible = true
}
})
stream.point(x, y)
}
arrayOfPoints.forEach((point, index) => {
streamWrapper(point.x, point.y, index)
})