Skip to content

Instantly share code, notes, and snippets.

@mitch-seymour
Last active March 14, 2016 20:49
Show Gist options
  • Save mitch-seymour/284943058903f05e5e97 to your computer and use it in GitHub Desktop.
Save mitch-seymour/284943058903f05e5e97 to your computer and use it in GitHub Desktop.
Geodesic satellites
height: 660
license: mit
(function() {
var width = 960,
height = 700,
scale = 240,
progress = 0,
inner_radius = (scale / 3.428);
var velocity = [-.003, .003];
var projection = d3.geo.orthographic()
.scale(scale)
.translate([width / 2, height / 2]);
var path = d3.geo.path()
.projection(projection);
// append the svg to the body
var g = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(0,0)")
.attr("class", "container");
// append the inner circles
g.append("circle")
.attr("class", "middle")
.attr("r", scale / 1.29)
.attr("cx", width / 2)
.attr("cy", height / 2);
g.append("circle")
.attr("class", "inner")
.attr("r", scale / 2)
.attr("cx", width / 2)
.attr("cy", height / 2);
// append the geodesic globe
var globeLines = g.append("path")
.attr("class", "lines")
.datum(d3.geodesic.multilinestring(6));
// append the innermost circle
g.append("circle")
.attr("class", "inner-most")
.attr("r", inner_radius)
.attr("cx", width / 2)
.attr("cy", height / 2);
// append the small circles with letters
var labels = ['V', 'P', 'D', 'S'];
for (var i = 0; i < 4; i++) {
var offset = scale / 2.86,
w = width / 2 + (scale / 4.8 * i) + offset;
var container = g.append('g')
.attr("class", function() {
return i == 0 ? "dc active" : "dc";
})
.attr("transform", "translate(" + w + "," + height / 2 + ")");
container.append("circle")
.attr("r", scale / 12);
container.append("text")
.attr("text-anchor", "middle")
.attr("dy", "4px")
.text(labels[i]);
}
// append the arc
var arc = d3.svg.arc()
.startAngle(0)
.innerRadius(scale / 4.53)
.outerRadius(scale / 4.21);
var arc_bg = g.append("g")
.attr("class", "arc-container")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
arc_bg.append("path")
.attr("class", "arc-background")
.attr("d", arc.endAngle(2 * Math.PI));
var foreground = arc_bg.append("path")
.attr("class", "arc-foreground");
var text = arc_bg.append("text")
.attr("class", "arc-text top")
.attr("text-anchor", "middle")
.attr("dy", "-3px")
.text("40");
arc_bg.append("text")
.attr("class", "arc-text bottom")
.attr("text-anchor", "middle")
.attr("dy", "15px")
.text("MB");
function addSatellite(angle) {
(function() {
var satellite_radius = 40,
offset = 90,
radius = scale + (satellite_radius * 2) + offset,
x1 = Math.cos((angle) * (Math.PI / 180)) * inner_radius + width / 2,
y1 = Math.sin((angle) * (Math.PI / 180)) * inner_radius + height / 2,
x2 = Math.cos((angle) * (Math.PI / 180)) * radius + width / 2,
y2 = Math.sin((angle) * (Math.PI / 180)) * radius + height / 2,
x2_line = Math.cos((angle) * (Math.PI / 180)) * (radius - satellite_radius) + width / 2,
y2_line = Math.sin((angle) * (Math.PI / 180)) * (radius - satellite_radius) + height / 2;
var satellite = g.append("g")
.attr("class", "satellite")
.attr("height", 100)
.attr("width", 100);
satellite
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
.transition().duration(1000)
.attr("transform", "translate(" + parseInt(x2) + "," + parseInt(y2) + ")");
// the line that connects the inner circle to the satellite circle
var line = g.append("line")
.attr("class", "satellite-connector")
.attr("x1", x1)
.attr("y1", y1)
.attr("x2", x1)
.attr("y2", y1);
line
.transition().duration(2000)
.attr("x2", x2_line)
.attr("y2", y2_line);
satellite.append("circle")
.attr("class", "satellite-circle")
.attr("r", 0)
.transition().duration(2400)
.attr("r", satellite_radius);
var _projection = d3.geo.orthographic()
.scale(80)
.translate([x2, y2]);
var _path = d3.geo.path()
.projection(_projection);
var _lines = g.append("path")
.attr("class", "satellite-path")
.datum(d3.geodesic.multilinestring(4));
d3.timer(function(elapsed) {
_projection.rotate([elapsed * velocity[0], elapsed * velocity[1]]);
_lines.attr("d", _path);
});
})();
}
function randomNumberBetween(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
function rotate() {
d3.timer(function(elapsed) {
projection.rotate([elapsed * velocity[0], elapsed * velocity[1]]);
d3.selectAll('.lines').attr("d", path);
});
}
function updateProgress(num) {
var num = num / 100,
i = d3.interpolate(progress, num);
d3.transition().duration(1000).tween("progress", function() {
return function(t) {
progress = i(t);
foreground.attr("d", arc.endAngle((2 * Math.PI) * progress));
text.text(d3.round(num * 100, 2));
};
});
}
// demo
updateProgress(10);
addSatellite(-35);
setInterval(function() {
updateProgress(randomNumberBetween(0, 100));
}, 1500);
rotate();
})();
// https://github.com/d3/d3-plugins
!function(){function n(n){return d3.merge(e.map(function(t){var o=r(t[0],t[1]),u=r(t[0],t[2]),i=[];i.push([t[0],o(1/n),u(1/n)]);for(var a=1;n>a;++a){for(var e=r(o(a/n),u(a/n)),c=r(o((a+1)/n),u((a+1)/n)),f=0;a>=f;++f)i.push([e(f/a),c(f/(a+1)),c((f+1)/(a+1))]);for(var f=0;a>f;++f)i.push([e(f/a),c((f+1)/(a+1)),e((f+1)/a)])}return i}))}function t(t){function r(n,t){var r;(n[0]<t[0]||n[0]==t[0]&&(n[1]<t[1]||n[1]==t[1]&&n[2]<t[2]))&&(r=n,n=t,t=r),u[n.map(o)+" "+t.map(o)]=[n,t]}function o(n){return d3.round(n,4)}var u={};return n(t).forEach(function(n){r(n[0],n[1]),r(n[1],n[2]),r(n[2],n[0])}),d3.values(u)}function r(n,t){var r=n[0],o=n[1],u=n[2],i=t[0]-r,a=t[1]-o,e=t[2]-u;return function(n){return[r+n*i,o+n*a,u+n*e]}}function o(n){var t=n[0],r=n[1],o=n[2];return[Math.atan2(r,t)*i,Math.acos(o/Math.sqrt(t*t+r*r+o*o))*i-90]}var u=1.618033988749895,i=180/Math.PI,a=[[1,u,0],[-1,u,0],[1,-u,0],[-1,-u,0],[0,1,u],[0,-1,u],[0,1,-u],[0,-1,-u],[u,0,1],[-u,0,1],[u,0,-1],[-u,0,-1]],e=[[0,1,4],[1,9,4],[4,9,5],[5,9,3],[2,3,7],[3,2,5],[7,10,2],[0,8,10],[0,4,8],[8,2,10],[8,4,5],[8,5,2],[1,0,6],[11,1,6],[3,9,11],[6,10,7],[3,11,7],[11,6,7],[6,0,10],[9,1,11]].map(function(n){return n.map(function(n){return a[n]})});d3.geodesic={multipolygon:function(t){return{type:"MultiPolygon",coordinates:n(~~t).map(function(n){return n=n.map(o),n.push(n[0]),n=[n]})}},polygons:function(n){return d3.geodesic.multipolygon(~~n).coordinates.map(function(n){return{type:"Polygon",coordinates:n}})},multilinestring:function(n){return{type:"MultiLineString",coordinates:t(~~n).map(function(n){return n.map(o)})}}}}();
<!doctype html>
<html>
<head>
<title>Geodesic satellites</title>
</head>
<body>
<style>
path {
fill: none;
stroke: rgba(139, 188, 253, .4);
}
.satellite-circle {
fill: #50D3C1;
fill-opacity: .88;
stroke: #50D3C1;
stroke-width: 2px;
}
.satellite-symbol {
fill: #fff;
fill-opacity: 1;
stroke: #50D3C1;
stroke-width: 2px;
}
.satellite-connector {
stroke: #50D3C1;
stroke-width: 2px;
}
.satellite-path {
stroke: #50D3C1;
stroke-opacity: .5;
}
.arc-background {
fill: #A8C3CE;
}
.dc circle {
fill: rgba(255, 255, 255, .7);
stroke: rgb(125, 181, 252);
stroke-width: 2px;
}
.dc.active circle {
fill: rgba(255, 255, 255, .98);
stroke: #02425F;
stroke-width: 3px;
}
.dc text {
fill: rgb(125, 181, 252);
font-family: Helvetica;
font-size: 12px;
}
.dc.active text {
fill: #02425F;
}
.arc-foreground {
fill: #fff;
}
.arc-text {
fill: #fff;
font-family: Helvetica;
}
.arc-text.bottom {
fill: #ccc;
font-style: italic;
font-size: 11px;
}
circle.outer {
fill: none;
stroke: rgba(139, 188, 253, .6);
stroke-width: 1px;
}
circle.middle {
fill: rgba(139, 188, 253, .6);
stroke: rgba(139, 188, 253, .8);
stroke-width: 2px;
}
circle.inner {
fill: rgba(124, 168, 246, .8);
stroke: rgba(124, 168, 246, .85);
stroke-width: 2px;
}
circle.inner-most {
fill: rgb(24, 95, 125);
}
</style>
<!-- scripts -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.12/d3.min.js"></script>
<script src="geodesic.min.js"></script>
<script src="geodesic-satellites.js"></script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment