I'm working of a fairly simple world globe interface using D3 and the D3.geo.projection to create a spinning globe with data points on it.
Everything worked fine (i.e. the points "eclipsed" when they rotated away behind the horizon) when I was just plotting the points with circles:
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)
;
But now I'm trying to plot symbols instead of circles:
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)
;
And while this works, and the symbols plot in the right place on the globe, they persist even when they wrap to the back of the globe. I can hide them with a visibility property if I had a way to determine if they were eclipsed, but I don't see a method in d3.geo.projection to do that. Any ideas?
Calculate if the point is visible
If you have your projection:
const chartProjection = d3.geo.orthographic();
You can turn it into a path function:
const path = d3.geo.path()
.projection(chartProjection);
Then you can evaluate each point for visibility. path
will return undefined
for values behind the projection.
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);
OK, so I was able to at least simulate the images hiding around the back of the globe, by calculating the great circle distance between the projection center and the point in question. If the distance was greater than π/2, the point would be beyond the horizon:
.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";
}
})
I'm sure I can get a bit fancier by fading them out as they approach the edge, disabling clicks, etc. but now I can move on...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With