This is a visualisation of our solar system’s planets. It's based on the tutorial from DATAMAKE.
Last active
July 10, 2019 05:58
-
-
Save sabrinamochi/94823908d4f921482e3f5891b8d2212c to your computer and use it in GitHub Desktop.
Planets
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> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<style> | |
.planet { | |
stroke-width: 4px; | |
} | |
#Sun { | |
fill: orange; | |
stroke: orange; | |
} | |
#Mercury { | |
stroke: #A17F5D; | |
fill: #A17F5D; | |
} | |
#Venus { | |
stroke: #E89624 fill:#E89624 | |
} | |
#Earth { | |
stroke: #518E87; | |
fill: #518E87; | |
} | |
#Mars { | |
stroke: #964120; | |
fill: #964120 | |
} | |
#Jupiter { | |
stroke: #F8800F; | |
fill: #F8800F; | |
} | |
#Saturn { | |
stroke: #E0B463; | |
fill: #E0B463; | |
} | |
#Uranus { | |
stroke: #D1E3F4; | |
fill: #D1E3F4; | |
} | |
#Neptune { | |
stroke: #515CA8; | |
fill: #515CA8; | |
} | |
body { | |
font-family: Arial, Helvetica, sans-serif; | |
font-size: 0.75rem; | |
background: radial-gradient(#091C33, #091426); | |
} | |
#title { | |
position: absolute; | |
top: 0; | |
left: 0; | |
width: 100%; | |
text-align: center; | |
color: #ddd; | |
} | |
a { | |
color: #ddd; | |
} | |
a:link { | |
text-decoration: none; | |
} | |
a:hover { | |
color: orange; | |
} | |
#pink { | |
border-bottom: 2px solid orange; | |
padding-bottom: 0.25rem; | |
} | |
.text { | |
fill: #ddd; | |
} | |
path.domain { | |
stroke: none; | |
} | |
.tick line { | |
stroke: #ddd; | |
stroke-width: 0.5; | |
shape-rendering: crispEdges; | |
stroke-dasharray: 1, 5; | |
} | |
.lines { | |
fill: none; | |
} | |
</style> | |
<title>planet</title> | |
<script type="text/javascript" src="https://d3js.org/d3.v5.min.js"></script> | |
<link rel="stylesheet" href="style.css"> | |
</head> | |
<body> | |
<h1 id='title'>Measuring Our Planets' | |
<span id='pink'> | |
<a href="https://en.wikipedia.org/wiki/Solar_System#Distances_and_scales" target="_blank">Distance </a> | |
</span> | |
</h1> | |
<div id='vis'> | |
</div> | |
<script type="text/javascript"> | |
function make(data) { | |
var margin = { | |
top: +500 * 0.3, | |
bottom: +500 * 0.4, | |
left: 50, | |
right: 50 | |
} | |
var h = 500 - margin.top - margin.bottom; | |
var maxDist = d3.max(data, function(d) { | |
return +d.distance; | |
}) | |
var mapScale = 1 / 10e4; | |
// full width of all planets | |
var chartWidth = maxDist * mapScale; | |
var screenWidth = 1000 - margin.left - margin.right; | |
var svg = d3.select('#vis') | |
.append('svg') | |
.attr('width', 1000) | |
.attr('height', 500) | |
.append('g') | |
.attr('class', 'chart') | |
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); | |
var listenerRect = svg.append('rect') | |
.attr('x', 0) | |
.attr('y', -margin.top) | |
.attr('width', screenWidth) | |
.attr('height', 500) | |
.style('opacity', 0); | |
var rScale = d3.scaleLinear() | |
.domain([0, d3.max(data, function(d) { | |
return d.radius; | |
})]) | |
.range([3, (h / 2) * 0.5]); | |
var xScale = d3.scaleLinear() | |
.domain([0, maxDist]) | |
.range([0, chartWidth]); | |
var xAxis = d3.axisBottom(xScale) | |
.tickSizeOuter(0) | |
.tickPadding(10) | |
// tickValues require an array, which can be made by data.map | |
.tickValues(data.map(function(e) { | |
return e.distance; | |
})) | |
// since tickFormat receives the values from tickValues | |
// d became d.distance | |
.tickFormat(function(d, i) { | |
return data[i].planet + ' ' + d3.format(',')(d) + ' km'; | |
}) | |
// since we want the zoom base to be on top of all other elements, | |
// we insert xaxis | |
// listenerRect should be the last child element of svg | |
var xAxisDraw = svg.insert('g', ':first-child') | |
.attr('class', 'x_axis') | |
.call(xAxis); | |
// getBBox() 用于获取元素范围的最小矩形边际 | |
// retrieve the height of text label | |
var labelHeight = xAxisDraw.select('text').node().getBBox().height; | |
xAxisDraw.attr('transform', 'translate(0,' + (labelHeight * data.length + h) + ')'); | |
xAxisDraw.selectAll('text') | |
.attr('class', 'text') | |
.attr('y', function(d, i) { | |
return -(i * labelHeight + labelHeight) | |
}) | |
//x、y 與 dx、dy 的差異: 前者為絕對位置,後者為相對位置 | |
.attr('dx', '-0.15em') | |
.attr('dy', '1.15em') | |
.style('text-anchor', 'start'); | |
xAxisDraw.selectAll('line') | |
.attr('y1', function(d, i) { | |
return -(i * labelHeight + labelHeight) | |
}) | |
.attr('y2', function(d, i) { | |
return -(i * labelHeight + labelHeight + | |
h + labelHeight * (data.length - 1 - i)) | |
}); | |
var gPlanets = svg.insert('g', '.listenerRect') | |
.attr('class', 'planets-group') | |
var planets = gPlanets.selectAll('.planet') | |
.data(data) | |
.enter() | |
.append('circle') | |
.attr('class', 'planet') | |
.attr('id', function(d) { | |
return d.planet; | |
}) | |
.attr('cx', function(d) { | |
return xScale(d.distance); | |
}) | |
.attr('cy', 0) | |
.attr('r', function(d) { | |
return rScale(d.radius) | |
}); | |
var minZoom = 1 / (chartWidth / 1000) * 0.75; | |
var zoom = d3.zoom() | |
.scaleExtent([minZoom, 20]) | |
.on('zoom', zoomed); | |
listenerRect.call(zoom); | |
function zoomed() { | |
var transform = d3.event.transform; | |
// to prevent the planets moving to the right | |
// x should never be higher than 0 | |
transform.x = Math.min(0, transform.x); | |
transform.y = 0; | |
var xScaleNew = transform.rescaleX(xScale); | |
// only zoom the cx and r | |
// so the stroke width wont be affected | |
planets.attr('cx', function(d) { | |
return xScaleNew(d.distance); | |
}) | |
.attr('r', function(d) { | |
return rScale(d.radius) * transform.k; | |
}); | |
// update the scale of xAxis and redraw it | |
xAxis.scale(xScaleNew); | |
xAxisDraw.call(xAxis); | |
xAxisDraw.selectAll('text') | |
.attr('y', function(d, i) { | |
return -(i * labelHeight + labelHeight) | |
}) | |
xAxisDraw.selectAll('line') | |
.attr('y1', function(d, i) { | |
return -(i * labelHeight + labelHeight) | |
}) | |
.attr('y2', function(d, i) { | |
return -(i * labelHeight + labelHeight + | |
h + labelHeight * (data.length - 1 - i)) | |
}); | |
} | |
// start by zooming in to a scale factor of 20 without panning | |
var initialTransform = d3.zoomIdentity.scale(20); | |
listenerRect.call(zoom.transform, initialTransform); | |
// after the initial zoom in... | |
// we zoom out a bit | |
progZoom() | |
function progZoom() { | |
var zoomOutTransform = d3.zoomIdentity | |
// there's no translation here. | |
.translate(0, 0) | |
.scale(minZoom); | |
listenerRect.transition() | |
.duration(5000) | |
.call(zoom.transform, zoomOutTransform) | |
.on('end', zoomToNormal); | |
function zoomToNormal() { | |
listenerRect.transition() | |
.duration(3000) | |
.ease(d3.easeQuadInOut) | |
.call(zoom.transform, d3.zoomIdentity) | |
} | |
} | |
} | |
var rowConverter = function(d) { | |
return { | |
index: +d.index, | |
planet: d.planet, | |
distance: +d.distance, | |
radius: +d.radius | |
}; | |
}; | |
d3.csv('https://raw.githubusercontent.com/sabrinamochi/planets_distance/master/planet.csv', rowConverter).then(function(data) { | |
console.table(data, ['planet', 'distance', 'radius']); | |
make(data); | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment