An attempt to use d3 to simulate planetary rings.
The same equitorial feature is drawn 10 times with projections that are increaslingly zoomed in and with a slightly enlarged clip radius.
This uses a looped append, which should normally not be used.
Based off of this rotating orthographic, upgraded to v4.
xxxxxxxxxx
<meta charset="utf-8">
<style>
path {
fill: none;
stroke-linejoin: round;
}
.sphere,
.graticule {
stroke: #aaa;
fill: white;
}
.equator {
stroke-width: 2px;
}
</style>
<body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var width = 960,
height = 500,
rotate = [10, -10],
velocity = [.003, -.001],
time = Date.now();
var projection = d3.geoOrthographic()
.scale(100)
.translate([width / 2, height / 2])
.rotate([20,-30,30])
.clipAngle(90 + 1e-6)
.precision(.3);
var colors = ["#ceb8b8","#ead6b8","#e3e0c0","#bfbdaf","#cecece"];
var dashArray = [25,1,10,1,12,1,28,1,17,1,35,1,5,1];
var path = d3.geoPath()
.projection(projection);
var projections = d3.range(10).map(function(d) {
var s = 100 * (1 + 0.25 + d/20 )
return d3.geoOrthographic()
.scale(s)
.translate([width / 2, height / 2])
.clipAngle(Math.pow(s/100,0.5)*90*1.25-Math.pow(d/5,2) + (1-Math.abs(0.5-d/5))*1 )
.precision(.3)
.rotate([20,-30,30])
})
var graticule = d3.geoGraticule();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var feature = {"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[179,0.1],[179,-0.1],[90,-0.1],[0,-0.1],[-90,-0.1],[-179,-0.1],[-179,0.1],[-90,0.1],[0,0.1],[90,0.1],[179,0.1]]]},"properties":null }
var sphere = svg.append("path")
.datum({type: "Sphere"})
.attr("class", "sphere")
.attr("d", path);
var grat = svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
d3.range(10).forEach(function(k) {
var ringsPath = d3.geoPath().projection(projections[k])
svg.append("path")
.datum(feature)
.attr("class","equator")
.attr("d",ringsPath)
.attr("stroke",colors[k%5])
.style("stroke-dasharray",dashArray);
})
var feature = svg.selectAll("path");
d3.timer(function() {
var dt = Date.now() - time;
projection.rotate([rotate[0] + velocity[0] * dt, rotate[1] + velocity[1] * dt]);
// update graticule and sphere
grat.attr("d", path);
sphere.attr("d",path);
// update the projection for each ring
projections.forEach(function(d,i) {
d.rotate( [(rotate[0] + 20 * i) + velocity[0] * dt, rotate[1] + velocity[1] * dt]);
})
// update each ring
d3.selectAll(".equator").each(function(d,i) {
var p = d3.select(this);
var r = projection.rotate();
var newPath = d3.geoPath().projection(projections[i])
p.attr("d",newPath);
})
});
</script>
https://d3js.org/d3.v4.min.js