Skip to content

Instantly share code, notes, and snippets.

@vsapsai
Last active February 10, 2018 07:05
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save vsapsai/dcdb09826749ccab62bb8390a4de8e80 to your computer and use it in GitHub Desktop.
SpaceX launches to different orbits. Distribution by day and hour
license: mit
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>SpaceX launches by time</title>
<style>
body {
font-family: Helvetica,Arial,sans-serif;
}
.orbit-label {
font-size: 20px;
}
.value-text {
font-size: 10px;
}
.bar {
fill: #849199; /* color picker from SpaceX logo */
}
</style>
</head>
<body>
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
margin = {top: 10, right: 20, bottom: 20, left: 10},
innerDistances = {orbitLabelWidth: 95, horizontal: 30, valueTextHeight: 10},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.json("https://api.spacexdata.com/v2/launches", function(error, data) {
if (error) {
throw error;
}
//console.log(data);
// Collect launches by orbit because we'll have graphs for each orbit.
var orbitData = data
.filter(function(launch) {
return (launch["rocket"]["rocket_id"] != "falcon1");
})
.reduce(function(orbitDict, launch) {
var orbit = launch["rocket"]["second_stage"]["payloads"][0]["orbit"];
var orbitLaunches = orbitDict[orbit];
if (!orbitLaunches) {
orbitLaunches = orbitDict[orbit] = [];
}
orbitLaunches.push(launch);
return orbitDict;
}, {});
//console.log(orbitData);
// Convert to array of dictionaries with all the required data.
var summarizedData = Object.keys(orbitData)
.map(function(orbit) {
var launches = orbitData[orbit];
var launchesByWeekDay = new Array(7).fill(0);
var launchesByHour = new Array(24).fill(0);
launches.forEach(function(launch) {
var launchDate = new Date(launch["launch_date_unix"] * 1000);
launchesByWeekDay[launchDate.getUTCDay()] += 1;
launchesByHour[launchDate.getUTCHours()] += 1;
});
return {
orbit: orbit,
firstFlightNumber: launches[0]["flight_number"], // for sorting
launches: launches,
launchesByWeekDay: launchesByWeekDay,
launchesByHour: launchesByHour,
};
});
// Order orbits in order of the first flight to such orbit.
summarizedData.sort(function(lhs, rhs) {
return lhs.firstFlightNumber - rhs.firstFlightNumber;
})
//console.log(summarizedData);
var maxLaunchesCount = d3.max(summarizedData, function(orbitData) {
return d3.max([
d3.max(orbitData.launchesByWeekDay),
d3.max(orbitData.launchesByHour)
]);
});
var plotWidth = (width - innerDistances.horizontal - innerDistances.orbitLabelWidth) / 2;
var xScaleWeekDays = d3.scaleBand()
.domain([1, 2, 3, 4, 5, 6, 0]) // put Sunday in the end
.rangeRound([0, plotWidth])
.paddingInner(0.1);
var xScaleHours = xScaleWeekDays.copy()
.domain(d3.range(24));
var yScaleOrbits = d3.scaleBand()
.domain(summarizedData.map(function(d) { return d.orbit; }))
.rangeRound([height, 0])
.paddingInner(0.1);
var orbitPlotHeight = yScaleOrbits.bandwidth(),
orbitPlotInnerHeight = orbitPlotHeight - innerDistances.valueTextHeight;
var yScaleValues = d3.scaleLinear()
.domain([0, maxLaunchesCount])
.rangeRound([orbitPlotInnerHeight, 0]);
g.selectAll("g")
.data(summarizedData)
.enter().append("g")
.attr("transform", function(orbitData) {
var yOffset = height - orbitPlotHeight - yScaleOrbits(orbitData.orbit);
return "translate(0," + yOffset + ")";
})
.each(buildOrbitPlots);
function buildOrbitPlots(orbitData) {
// Orbit name.
var labelText = d3.select(this).append("g")
.append("text")
.attr("x", innerDistances.orbitLabelWidth / 2)
.attr("y", orbitPlotHeight / 2)
.attr("text-anchor", "middle")
.attr("class", "orbit-label");
var words = orbitData.orbit.split(" ")
.filter(function(w) { return w.length > 0; });
if (words.length > 1) {
labelText.selectAll("tspan")
.data(words)
.enter().append("tspan")
.attr("x", innerDistances.orbitLabelWidth / 2)
.attr("dy", function(d, i) { return i > 0 ? "1.2em" : 0; })
.text(function(d) { return d; });
} else {
labelText.text(orbitData.orbit);
}
var weekDayG = d3.select(this).append("g")
.attr("transform", "translate(" + innerDistances.orbitLabelWidth + ",0)");
var weekDayBars = weekDayG.selectAll("g")
.data(orbitData.launchesByWeekDay)
.enter().append("g")
.attr("transform", function(d, i) {
var x = xScaleWeekDays(i),
y = yScaleValues(d) + innerDistances.valueTextHeight;
return "translate(" + x + "," + y + ")";
});
weekDayBars.append("rect")
.attr("class", "bar")
.attr("x", 0)
.attr("y", 0)
.attr("width", xScaleWeekDays.bandwidth())
.attr("height", function(d) { return orbitPlotInnerHeight - yScaleValues(d); })
weekDayBars.append("text")
.attr("x", xScaleWeekDays.bandwidth() / 2)
.attr("y", -2)
.attr("text-anchor", "middle")
.attr("class", "value-text")
.text(function(d) { return d > 0 ? d : ""; });
var hoursG = d3.select(this).append("g")
.attr("transform", "translate(" + (width-plotWidth) + ",0)");
var hoursBars = hoursG.selectAll("g")
.data(orbitData.launchesByHour)
.enter().append("g")
.attr("transform", function(d, i) {
var x = xScaleHours(i),
y = yScaleValues(d) + innerDistances.valueTextHeight;
return "translate(" + x + "," + y + ")";
});
hoursBars.append("rect")
.attr("class", "bar")
.attr("x", 0)
.attr("y", 0)
.attr("width", xScaleHours.bandwidth())
.attr("height", function(d) { return orbitPlotInnerHeight - yScaleValues(d); })
hoursBars.append("text")
.attr("x", xScaleHours.bandwidth() / 2)
.attr("y", -2)
.attr("text-anchor", "middle")
.attr("class", "value-text")
.text(function(d) { return d > 0 ? d : ""; });
// Add axes.
var isLastOrbit = (orbitData === summarizedData[summarizedData.length - 1]);
var WEEKDAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
weekDayG.append("g")
.attr("transform", "translate(0," + orbitPlotHeight + ")")
.call(d3.axisBottom(xScaleWeekDays)
.tickSize(0)
.tickPadding(5)
.tickFormat(function(d) {
// Don't repeat day names over and over, show only at the very bottom.
return isLastOrbit ? WEEKDAYS[d] : "";
}));
hoursG.append("g")
.attr("transform", "translate(0," + orbitPlotHeight + ")")
.call(d3.axisBottom(xScaleHours)
.tickSize(0)
.tickPadding(5)
// But for hours it is more usable (though uglier) to have
// ticks for each plot. Otherwise it is hard to match bar with
// hour value.
.tickFormat(d3.format("02d")));
}
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment