Last active
October 23, 2019 22:50
-
-
Save Mavromatika/cc5b26d140e197f55f9e to your computer and use it in GitHub Desktop.
Exploring the Solar System
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ | |
{"planet":"Mercury","radius":2439.7,"moons":[],"nmoons":0}, | |
{"planet":"Venus","radius":6051.8,"moons":[],"nmoons":0}, | |
{"planet":"Earth","radius":6371.0,"moons":[{"moon":"Moon","radius":1737.5}],"nmoons":1}, | |
{"planet":"Mars","radius":3389.5,"moons":[{"moon":"Phobos","radius":11.1},{"moon":"Deimos","radius":6.2}],"nmoons":2}, | |
{"planet":"Ceres","radius":476.2,"moons":[],"nmoons":0}, | |
{"planet":"Jupiter","radius":69911,"moons":[{"moon":"Io","radius":1821.6},{"moon":"Europa","radius":1560.8},{"moon":"Ganymede","radius":2631.2},{"moon":"Callisto","radius":2410.3}],"nmoons":67}, | |
{"planet":"Saturn","radius":58232,"moons":[{"moon":"Titan","radius":2574.7},{"moon":"Iapetus","radius":735.6},{"moon":"Rhea","radius":764.3},{"moon":"Dione","radius":561.7},{"moon":"Tethys","radius":533.0}],"nmoons":62}, | |
{"planet":"Uranus","radius":25362,"moons":[{"moon":"Oberon","radius":761.4},{"moon":"Titania","radius":788.9},{"moon":"Ariel","radius":578.9},{"moon":"Umbriel","radius":584.7}],"nmoons":27}, | |
{"planet":"Neptune","radius":24622,"moons":[{"moon":"Triton","radius":1353.4}],"nmoons":14}, | |
{"planet":"Pluto","radius":1151,"moons":[{"moon":"Charon","radius":603.6}],"nmoons":5}, | |
{"planet":"Eris","radius":0,"moons":[{"moon":"Dysnomia","radius":0}],"nmoons":1}, | |
{"planet":"Haumea","radius":0,"moons":[{"moon":"Namaka","radius":0},{"moon":"Hi'iaka","radius":0}],"nmoons":2}, | |
{"planet":"Makemake","radius":0,"moons":[],"nmoons":0} | |
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
body { | |
background: #000000; | |
font-family: sans-serif; | |
} | |
h1, h2 { | |
color: #eaeaea; | |
text-align: center; | |
} | |
.first-text { | |
color: #eaeaea; | |
font-size: 80%; | |
width: 300px; | |
text-align: justify; | |
margin-left: auto; | |
margin-right: auto; | |
margin-bottom: 60px; | |
} | |
.source { | |
font-size: 70%; | |
} | |
.source a { | |
color: #eaeaea; | |
} | |
.schema { | |
width: 1300px; | |
margin-left: auto; | |
margin-right: auto; | |
margin-bottom: 60px; | |
} | |
.planet{ | |
width: 1200px; | |
position: relative; | |
margin-left: auto; | |
margin-right: auto; | |
} | |
.svgPlanet{ | |
position: absolute; | |
top: 0; | |
left: 50%; | |
/*padding-left: 0; | |
padding-right: 0; | |
margin-left: auto; | |
margin-right: auto; | |
display: block;*/ | |
} | |
canvas { | |
padding-left: 0; | |
padding-right: 0; | |
margin-left: auto; | |
margin-right: auto; | |
display: block; | |
} | |
</style> | |
<body> | |
<script src="//d3js.org/d3.v3.min.js"></script> | |
<!--<script src="//d3js.org/d3.geo.projection.v0.min.js"></script>--> | |
<h1>Exploring the Solar System</h1> | |
<h2>The planets and their moons</h2> | |
<div class="first-text"> | |
<p>The graphic below shows the known planets of the Solar System and their main satellites (radius > 500km). Their sizes are proportional, except for the smallest objects (r ~ 500 km).</p> | |
<p class="source">Source : <a href="https://solarsystem.nasa.gov/planets/solarsystem">NASA</a></p> | |
</div> | |
<div class="schema"></div> | |
<h2>Where probes and landers have landed</h2> | |
<div class="first-text"> | |
<p>Hi-resolution images from NASA were projected on the globes below. The places where landers or probes have reached the surface (by landing or crashing...) are represented by dots.</p> | |
<p>The globes can be rotated by dragging them.</p> | |
<p class="source">Sources : NASA for <a href="http://nssdc.gsfc.nasa.gov/planetary">landing coordinates</a>, and for the maps of <a href="http://maps.jpl.nasa.gov/venus.html">Venus</a>, <a href="http://www.mars.asu.edu/data/mdim_color/">Mars</a>, and the <a href="http://astrogeology.usgs.gov/search/details/Moon/Clementine/UVVIS/Lunar_Clementine_UVVIS_750nm_Global_Mosaic_118m_v2/cub">Moon</a>.</p> | |
</div> | |
<div id="content"></div> | |
<script> | |
var width = 1200, height = 600; // of the images (for the looping through pixels) | |
var W = 960, H = 560; // of the planet containers | |
var schemaW = 1300, schemaH = 300; // of the schema container for the size comparison | |
var landingR = 4; | |
var projScale =250; | |
var labelSpacing = 40; | |
var padL = 160, padR = 160, padB = 0, paddingT = 30; | |
var projection = d3.geo.orthographic().scale(projScale).translate([W/2,H/2]); | |
var compXScale = d3.scale.ordinal().range([0,schemaW]); | |
var compRScale = d3.scale.linear().range([1,schemaH/2]); | |
var λ = d3.scale.linear() | |
.domain([0, W]) | |
.range([-180, 180]); | |
var drag = d3.behavior.drag().on("drag",dragmove).on("dragend",dragend); | |
var schemaContainer = d3.select(".schema"); | |
var svgSchema = schemaContainer.append("svg").attr("width",schemaW).attr("height",schemaH); | |
// A function called when images load, with the image passed as parameter. | |
var onload = function(pic) { | |
return function(){ | |
pic.dataImage.dx = pic.width, | |
pic.dataImage.dy = pic.height; | |
pic.canvas.attr("width",pic.dataImage.dx).attr("height",pic.dataImage.dy); | |
pic.context.drawImage(pic, 0, 0, pic.dataImage.dx, pic.dataImage.dy); // Draw the image once on canvas | |
pic.dataImage.sourceData = pic.context.getImageData(0, 0, pic.dataImage.dx, pic.dataImage.dy).data, // get the data (pixels) | |
pic.dataImage.target = pic.context.createImageData(pic.dataImage.dx, pic.dataImage.dy), // create a new, empty image on the canvas | |
pic.dataImage.targetData = pic.dataImage.target.data; | |
pic.canvas.attr("width", W).attr("height",H); | |
display(pic, 0); | |
} | |
} | |
// Create all the images of the planets | |
var sources = [{"name":"Mars","src":"mars-small.jpg","landings":[{"id":0,"mission":"Viking 1","coord":[-49.97,22.48]}, | |
{"id":1,"mission":"Viking 2","coord":[-225.74,47.97]}, | |
{"id":2,"mission":"Mars 2","coord":[47,-45]}, | |
{"id":3,"mission":"Mars 3","coord":[-158,-45]}, | |
{"id":4,"mission":"Mars 6","coord":[-19.42,-23.90]}, | |
{"id":5,"mission":"Mars Pathfinder","coord":[-33.55,19.33]}, | |
{"id":6,"mission":"Spirit","coord":[175.478,-14.572]}, | |
{"id":7,"mission":"Opportunity","coord":[5.527,-1.946]}, | |
{"id":8,"mission":"Phoenix Mars Lander","coord":[-125.9,68.15]}, | |
{"id":9,"mission":"Curiosity","coord":[137.4,-4.5]}, | |
]}, | |
//{"name":"Jupiter","src":"jupiter-small.jpg"}, | |
{"name":"Venus","src":"venus-small.jpg","landings":[{"id":0,"mission":"Pioneer Large Probe","coord":[-56,4.4]}, | |
{"id":1,"mission":"Pioneer North Probe","coord":[4.8,59.3]}, | |
{"id":2,"mission":"Pioneer Day Probe","coord":[-43,-31.3]}, | |
{"id":3,"mission":"Pioneer Night Probe","coord":[56.7,28.7]}, | |
{"id":4,"mission":"Venera 4","coord":[38,19]}, | |
{"id":5,"mission":"Venera 5","coord":[18,-3]}, | |
{"id":6,"mission":"Venera 6","coord":[23,-5]}, | |
{"id":7,"mission":"Venera 7","coord":[-9,-5]}, | |
{"id":8,"mission":"Venera 8","coord":[-25,-10]}, | |
{"id":9,"mission":"Venera 9","coord":[-69,31.7]}, | |
{"id":10,"mission":"Venera 10","coord":[-69,16]}, | |
{"id":11,"mission":"Venera 11","coord":[-61,-14]}, | |
{"id":12,"mission":"Venera 12","coord":[-66,-7]}, | |
{"id":13,"mission":"Venera 13","coord":[-57,-7.5]}, | |
{"id":14,"mission":"Venera 14","coord":[-50,-13.15]} | |
]}, | |
{"name":"Moon","src":"moon-small.jpg","landings":[{"id":0,"mission":"Apollo 11","coord":[23.47298,0.67409]}, | |
{"id":1,"mission":"Apollo 12","coord":[-23.41930,-3.01381]}, | |
{"id":2,"mission":"Apollo 14","coord":[-17.47139,-3.64544]}, | |
{"id":3,"mission":"Apollo 15","coord":[3.63400,26.13224]}, | |
{"id":4,"mission":"Apollo 16","coord":[15.49859,-8.97341]}, | |
{"id":5,"mission":"Apollo 17","coord":[30.77475,20.18809]}, | |
]} | |
//{"name":"Earth","src":"earth-small.jpg"}, | |
//{"name":"Mercury","src":"mercury-small.jpg"}, // No landings | |
//{"name":"Saturn","src":"saturn-small.jpg"} | |
]; | |
var images = []; | |
// Create all the image objects from the data in "sources" | |
for (var i = 0; i < sources.length; i++){ | |
var cont = d3.select("#content").append("div").attr("class","planet"); | |
var canv = cont.append("canvas"); | |
images.push(new Image); | |
var it = images[i]; | |
it.container = cont; | |
it.canvas = canv; | |
it.context = canv.node().getContext("2d"); | |
it.dataImage = {sourceData : [], target : Object, targetData : [], dx : 0, dy : 0}; | |
it.svg = cont.append("svg").attr("width",W).attr("height",H).attr("class","svgPlanet").style("margin-left", "-" + (W/2).toString() + "px"); | |
if (sources[i].landings){ | |
it.landings = sources[i].landings; | |
} | |
it.angle = 0; | |
it.onload = onload(images[i]); | |
it.src = sources[i].src; | |
// A title, and a transparent circle on top to handle drag events. | |
it.svg.append("text").attr("x",W/2).attr("y",H-10).text(sources[i].name).attr("fill","#eaeaea").attr("text-anchor","middle"); | |
it.svg.append("circle").attr("cx",W/2).attr("cy",H/2) | |
.attr("r",projScale) | |
.attr("fill","transparent") | |
.attr("class","circdrag") | |
.attr("id","i" + i.toString()); | |
// Position points if landings exist. | |
if (it.landings){ | |
it.groups = it.svg.selectAll(".landings") | |
.data(it.landings, function(d){return d.id;}) // Necessary to keep track of el when sorting | |
.enter() | |
.append("g") | |
.attr("class","landings"); | |
it.circles = it.groups.append("circle").attr("class","circland") | |
.attr("cx", function(d){return projection(d.coord)[0];}) | |
.attr("cy", function(d){return projection(d.coord)[1];}) | |
.attr("r",landingR) | |
.attr("fill","#eaeaea") | |
.attr("stroke","black") | |
.attr("title",function(d){return d.coord[0].toString() + " , " + d.coord[1].toString();}); | |
it.lines = it.groups.append("line") | |
.attr("class","lineland") | |
.attr("x1",padL) | |
.attr("y1",0) | |
.attr("x2",function(d){return projection(d.coord)[0];}) | |
.attr("y2",function(d){return projection(d.coord)[1];}) | |
.attr("stroke","#eaeaea"); | |
it.labels = it.groups.append("text") | |
.attr("fill","#eaeaea") | |
.text(function(d){return d.mission;}); | |
} | |
} | |
// Create an array with only the visible points given the projection (to loop through later). | |
var toLoop = [], k = -1; | |
for (var y = 0; y < height; ++y) { | |
for (var x = 0; x < width; ++x) { | |
var e = projection.invert([x,y]); | |
if (!isNaN(e[0])) { | |
toLoop.push([e[0],e[1],k]); | |
} | |
k += 4; | |
} | |
} | |
//--------------------------------------------------------------------------------------------------------------------------------------- | |
// Schema of the planets for size comparison | |
d3.json("datasize.json", function(error, data){ | |
if (error) return console.warn(error); | |
compRScale.domain([d3.min(data, function(d){return +d.radius;}), | |
d3.max(data, function(d){return +d.radius;})]); | |
// Compute the space between planets and their position on x axis. | |
var sum = 0; | |
for (i = 0;i < data.length; i++){ | |
sum += compRScale(data[i].radius); | |
} | |
var space = ((schemaW-30) - (sum*2)) / (data.length+1); | |
var spaceArray = [], memo = 0; | |
for (i = 0;i < data.length; i++){ | |
memo = memo + space + compRScale(data[i].radius); | |
spaceArray.push(memo); | |
memo += compRScale(data[i].radius); | |
} | |
var dataMoons = []; | |
for (i=0;i<data.length;i++){ | |
if (data[i].nmoons!=0){ | |
for (j=0;j<data[i].moons.length;j++){ | |
dataMoons.push({"index" : i, "ypos" : j, "data" : data[i].moons[j]}); | |
} | |
} | |
} | |
var plaComp = svgSchema.selectAll(".planetComparison") | |
.data(data) | |
.enter() | |
.append("circle"); | |
var txtComp = svgSchema.selectAll(".textComparison") | |
.data(data) | |
.enter() | |
.append("text"); | |
var satComp = svgSchema.selectAll(".satComparison") | |
.data(dataMoons) | |
.enter() | |
.append("circle"); | |
var txtSatComp = svgSchema.selectAll(".textSatComparison") | |
.data(dataMoons) | |
.enter() | |
.append("text"); | |
plaComp.attr("class","planetComparison") | |
.attr("cx",function(d,i){return spaceArray[i];}) | |
.attr("cy",150) | |
.attr("r",function(d){return compRScale(+d.radius);}) | |
.attr("stroke","#a3a3a3"); | |
satComp.attr("class","satComparison") | |
.attr("cx",function(d,i){return spaceArray[d.index];}) | |
.attr("cy",function(d){return 180 + d.ypos*20;}) | |
.attr("r",function(d){return compRScale(+d.data.radius);}) | |
.attr("stroke","#a3a3a3"); | |
txtComp.attr("class","textComparison") | |
.attr("x",function(d,i){return spaceArray[i];}) | |
.attr("y",schemaH/2 - 15) | |
.attr("fill","white") | |
.attr("text-anchor","start") | |
.attr("transform",function(d,i){ | |
return "rotate(-40 " + spaceArray[i].toString() + " " + (schemaH/2 - 15).toString() + ")"; | |
}) | |
.text(function(d){return d.planet;}); | |
txtSatComp.attr("class","textSatComparison") | |
.attr("x",function(d,i){return spaceArray[d.index] - 10;}) | |
.attr("y",function(d){return 180 + d.ypos*20;}) | |
.attr("fill","white") | |
.attr("text-anchor","end") | |
.attr("transform",function(d,i){ | |
return "rotate(-40 " + spaceArray[d.index].toString() + " " + (180 + d.ypos*20).toString() + ")"; | |
}) | |
.style("font-size","12px") | |
.text(function(d){return d.data.moon;}); | |
}); | |
//----------------------------------------------------------------------------------------------------------------------------- | |
// Function that displays the planets. | |
function display(image, angle){ | |
for (var j = 0; j < toLoop.length; ++j){ | |
var λ = toLoop[j][0], φ = toLoop[j][1]; | |
var i = toLoop[j][2]; | |
// Equivalent to : (numRow * width + numCol ) * 2^2 , or : numRow * width * 4 + numCol * 4. | |
// See here : https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas | |
var q = ((90 - φ) / 180 * image.dataImage.dy | 0) * image.dataImage.dx + ((180 + λ + angle) / 360 * image.dataImage.dx | 0) << 2; | |
image.dataImage.targetData[++i] = image.dataImage.sourceData[q]; | |
image.dataImage.targetData[++i] = image.dataImage.sourceData[++q]; | |
image.dataImage.targetData[++i] = image.dataImage.sourceData[++q]; | |
image.dataImage.targetData[++i] = 255; | |
} | |
//context.clearRect(0, 0, width, height); | |
image.context.putImageData(image.dataImage.target, 0, 0); | |
if (image.landings){ | |
image.circles.attr("r", 2) | |
.attr("cx", function(d){return projection([d.coord[0]-angle,d.coord[1]])[0];}) | |
.attr("r", function(d){ | |
var pos = (d.coord[0]-angle) % 360; | |
if ( ((pos > 90) && (pos < 270)) || ((pos < -90) && (pos > -270)) ){ | |
return 0; | |
} | |
else {return landingR;} | |
}); | |
// Update data of the groups (to position lines and boxes) | |
image.groups.each(function(d){ | |
var pos = (d.coord[0]-angle) % 360; | |
// lateral | |
if ( (pos > 0 && pos <=90) || (pos < -270 && pos >= -360)) { | |
d3.select(this).data()[0].lateral = "right"; | |
} | |
else { | |
d3.select(this).data()[0].lateral = "left"; | |
} | |
// visibility | |
if ( ((pos > 90) && (pos < 270)) || ((pos < -90) && (pos > -270)) ){ | |
d3.select(this).data()[0].visibility = "hidden"; | |
} | |
else { | |
d3.select(this).data()[0].visibility = "visible";; | |
} | |
}); | |
// Update position and visibility of lines | |
image.lines.attr("x2",function(d){ | |
return projection([d.coord[0]-angle,d.coord[1]])[0]; | |
}) | |
.attr("x1",function(d){ | |
if (d.lateral == "right"){ return W - padR;} | |
else { return padL;} | |
}) | |
.attr("visibility",function(d){ | |
return d.visibility; | |
}); | |
// Update position and visibility of lines | |
image.labels.attr("x",function(d){ | |
if (d.lateral == "right"){return W - padR;} | |
else {return padL;} | |
}) | |
.attr("text-anchor",function(d){ | |
if (d.lateral == "right"){return "start";} | |
else {return "end";} | |
}) | |
.attr("visibility",function(d){ | |
return d.visibility; | |
}); | |
// Sort elements on the right | |
var right = image.groups.filter(function(d){ | |
return d.lateral == "right" && d.visibility == "visible"; | |
}).sort(function(a, b){ | |
return d3.descending(a.coord[1], b.coord[1]); | |
}); | |
right.select("line") | |
.attr("y1",function(d,i){return i*labelSpacing + paddingT;}); | |
right.select("text") | |
.attr("y",function(d,i){return i*labelSpacing + paddingT;}); | |
// Sort elements on the left | |
var left = image.groups.filter(function(d){ | |
return d.lateral == "left" && d.visibility == "visible"; | |
}).sort(function(a, b){ | |
return d3.descending(a.coord[1], b.coord[1]); | |
}); | |
left.select("line") | |
.attr("y1",function(d,i){return i*labelSpacing + paddingT;}); | |
left.select("text") | |
.attr("y",function(d,i){return i*labelSpacing + paddingT;}); | |
} | |
} | |
// Does the moving | |
d3.selectAll(".circdrag").call(drag); | |
var angle = 0; | |
var flag = 0; | |
var orig = []; | |
function dragmove(d) { | |
if (flag == 0){ | |
orig = [d3.event.x,d3.event.y]; | |
flag = 1; | |
} | |
if (!isNaN(projection.invert([d3.event.x,d3.event.y])[0])){ | |
var dif = projection.invert(orig)[0] - projection.invert([d3.event.x,d3.event.y])[0]; | |
} | |
else {return;} | |
var id = d3.select(this).attr("id"); | |
id = id.substring(1, id.length); | |
angle = (images[id].angle + dif) % 360; | |
display(images[id], angle); | |
} | |
function dragend(d){ | |
var id = d3.select(this).attr("id"); | |
id = id.substring(1, id.length); | |
images[id].angle = angle; | |
flag = 0; | |
} | |
d3.selectAll(".circland").on("click", function(){ | |
//var parent = d3.select(this.parentNode); | |
//parent.select("line").classed("high","true"); | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment