Published
Edited
Oct 12, 2020
1 fork
3 stars
Insert cell
Insert cell
Insert cell
airlineRawData = d3.csv("https://magrawala.github.io/cs448b-fa20/assets/docs/airlines.csv", function(d) {
return {
airline: d.airline,
date: /* Fill in */, // We need to parse the date string into a Date object using d3.timeParse. Here, the string looks like 10/15/19 (%m/%d/%y).
price: +d.price
};
}).then(function(airlineRawData) {
//Outside of Observable notebooks you would put all code to draw the graph here.
return airlineRawData;
});
Insert cell
printTable(airlineRawData.slice(0, 10, 5))
Insert cell
Insert cell
airlineData = [{airline: "AAL", values: []}, {airline: "UAL", values: []}, {airline: "DAL", values: []}]
Insert cell
Insert cell
airlineRawData.forEach(function(d) {
airlineData[["AAL", "UAL", "DAL"].indexOf(d.airline)].values.push({date: d.date, price: d.price})
});
Insert cell
Insert cell
d3.extent(airlineRawData, d => d.date);
Insert cell
Insert cell
Insert cell
color = ({"AAL": "gray", "UAL": "steelblue", "DAL": "firebrick"});
Insert cell
Insert cell
Insert cell
Insert cell
plotVars = ({
plotWidth: 600, // Width of plot region
plotHeight: 300, // Height of plot region
plotMargin: 50 // Margin space for axes and their labels
});
Insert cell
Insert cell
Insert cell
xScale = d3.scaleTime()
.domain(/* Fill in */) // Try using d3.extent(), which gives [min, max]. We want to know the date ranges among the dates
.range(/* Fill in */);
Insert cell
yScale = d3.scaleLinear()
.domain([0, /* Fill in */]) // Try using d3.max(). We want the max of the price
.range(/* Fill in */); // Don't forget to flip as the origin of the SVG is at top, left
Insert cell
Insert cell
Insert cell
{
let svgPlotContainer = d3.create("svg")
.attr("width", 250)
.attr("height", 250)
.style("background-color", "whitesmoke"); // Color container bg to see its extent
svgPlotContainer.append("path")
.attr("d", "M50,10L75,200L225,100")
.attr("fill", "none") // Do not fill the area defined by the path (Try erasing this if you are curious)
.attr("stroke", "steelblue"); // Set a color for the line
return svgPlotContainer.node();
}
Insert cell
Insert cell
lineGenerator = d3.line()
.x(d => /* Fill in */)) // date should go on the x-axis
.y(d => /* Fill in */); // price should go on the y-axis
Insert cell
lineGenerator(airlineData[0].values)
Insert cell
Insert cell
function drawLines(airlineData, plotContainer) {
plotContainer.append("g")
.selectAll("path")
.data(airlineData)
.join("path")
.attr("class", "data-line") // Class name to be able to access the lines later
.attr("d", d => /* Fill in */) // Use lineGenerator on d.values
.attr("fill", "none") // Do not fill the area defined by the path
.attr("stroke", d => /* Fill in */); // Set a color for the line (The maps for color is in 'color')
}
Insert cell
{
let svgPlotContainer = d3.create("svg")
.attr("width", plotVars.plotWidth)
.attr("height", plotVars.plotHeight)
.style("background-color", "whitesmoke"); // Color container bg to see its extent
// drawLines(airlineData, svgPlotContainer); // Uncomment this to see the result
return svgPlotContainer.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
let svgPlotContainer = d3.create("svg")
.attr("width", 250)
.attr("height", 250)
.style("background-color", "whitesmoke"); // Color container bg to see its extent
const brush = d3.brush();
svgPlotContainer.call(brush); // Attach the brush to svgPlotContainer
return svgPlotContainer.node();
}
Insert cell
Insert cell
function brushed(lines) {
return function({selection}) {
if (selection) {
const [[xMin, yMin], [xMax, yMax]] = selection; // Unpack the bounding box of the selection
if (xMin === xMax && yMin === yMax) {
// The selection box is empty
lines.style("opacity", 1);
} else {
lines.style("opacity", 0.2) // Start by setting all opacity to 0.2
.filter(d =>
/* Fill in */) // Filter for lines that have at least one point within the rectangle. Feel free to change this to function(d) {operations; return something;} if you cannot get the code for the filtering in one line.
.style("opacity", 1);
}
} else {
// Nothing has been selected yet
lines.style("opacity", 1);
}
}
}
Insert cell
Insert cell
Insert cell
function addBrush(brushContainer, lines) {
const brush = d3.brush()
.on("start brush end", brushed(lines))
.extent(/* Fill in */); // Sets the brushable area [[xMin, yMin], [xMax, yMax]]
brushContainer.call(brush);
}
Insert cell
Insert cell
{
let svgPlotContainer = d3.create("svg")
.attr("width", plotVars.plotWidth)
.attr("height", plotVars.plotHeight)
.style("background-color", "whitesmoke"); // Color container bg to see its extent
drawLines(airlineData, svgPlotContainer);
let brushContainer = svgPlotContainer.append("g");
addBrush(brushContainer, svgPlotContainer.selectAll("path.data-line"));

return svgPlotContainer.node();
}
Insert cell
Insert cell
Insert cell
{
let svgPlotContainer = d3.create("svg")
.attr("width", 250)
.attr("height", 250);
const brush = d3.brush();
svgPlotContainer.call(brush); // Attach the brush to svgPlotContainer
// Color overlay rectangle with firebrick
svgPlotContainer.selectAll("rect.overlay")
.style("fill", "firebrick")
.style("opacity", 0.5);
// Color the selection rectangle
svgPlotContainer.selectAll("rect.selection")
.style("fill", "steelblue")
.style("opacity", 0.5);
// Color the handles
svgPlotContainer.selectAll("rect.handle")
.style("fill", "gray")
.style("opacity", 0.5);
return svgPlotContainer.node();
}
Insert cell
Insert cell
Insert cell
{
let svgPlotContainer = d3.create("svg")
.attr("width", 250)
.attr("height", 250)
.style("background-color", "whitesmoke"); // Color container bg to see its extent
const brush = d3.brush()
.on("end", function() {
/* Fill in */
}); // Remove the overlay when the selection is completed.
svgPlotContainer.call(brush); // Attach the brush to svgPlotContainer
return svgPlotContainer.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function addMoveCallback(rect) {
function moved(event) {
rect.attr("x", /* Fill in */) // Shift the x value by event.dx
.attr("y", /* Fill in */); // Shift the y value by event.dy
}

// Attach callback to rect
rect.call(d3.drag()
.on("drag", /* Fill in */)); // Use our 'moved' function
}
Insert cell
Insert cell
{
let svgPlotContainer = d3.create("svg")
.attr("width", 250)
.attr("height", 250)
.style("background-color", "whitesmoke"); // Color container bg to see its extent
let rect = svgPlotContainer.append("rect")
.attr("x", 100)
.attr("y", 100)
.attr("width", 50)
.attr("height", 50)
.style("fill", "firebrick")
.style("cursor", "all-scroll"); // Change the cursor to show that the rect can be moved.
addMoveCallback(rect);
return svgPlotContainer.node();
}
Insert cell
Insert cell
function addResizeCallback(rect, controlRect) {
let initialMouseY;
let initialRectY;
let initialRectHeight;
// Obtain the initial rectangle and mouse status
function resizeStarted(event) {
initialMouseY = event.y;
initialRectY = +rect.attr("y");
initialRectHeight = +rect.attr("height");
}
function resized(event) {
let minY = /* Fill in */; // Compute the minimum value of y of the rect
let maxY = /* Fill in */; // Compute the maximum value of y of the rect
rect.attr("y", /* Fill in */) // set the new y of the rect
.attr("height", /* Fill in */); // set the new height of the rect
controlRect.attr("y", initialRectY + event.y - initialRectY - 3); // Set the new y of the control
}
// Always keep the top control on top
function resizeEnded(event) {
let minY = Math.min(initialRectY + event.y - initialMouseY, initialRectY + initialRectHeight);
controlRect.attr("y", minY - 3);
}

// Attach callback to controlRect
controlRect.call(d3.drag()
.on("start", resizeStarted)
.on("drag", resized)
.on("end", resizeEnded));
}
Insert cell
Insert cell
function addMoveCallbackWithControl(rect, controlRect) {
function moved(event) {
rect.attr("x", +rect.attr("x") + event.dx) // Shift the x value by event.dx
.attr("y", +rect.attr("y") + event.dy); // Shift the y value by event.dy
controlRect.attr("x", /* Fill in */) // Shift the x value by event.dx
.attr("y", /* Fill in */); // Shift the y value by event.dy
}

// Attach callback to rect
rect.call(d3.drag()
.on("drag", moved));
}
Insert cell
Insert cell
{
let svgPlotContainer = d3.create("svg")
.attr("width", 250)
.attr("height", 250)
.style("background-color", "whitesmoke"); // Color container bg to see its extent
let rect = svgPlotContainer.append("rect")
.attr("x", 100)
.attr("y", 100)
.attr("width", 50)
.attr("height", 50)
.style("fill", "firebrick")
.style("cursor", "all-scroll");
let controlRect = svgPlotContainer.append("rect")
.attr("x", 100)
.attr("y", 100 - 3)
.attr("width", 50)
.attr("height", 6) // Set the thickness to 6.
.style("fill", "black")
.style("cursor", "ns-resize");
addMoveCallbackWithControl(rect, controlRect);
addResizeCallback(rect, controlRect);
return svgPlotContainer.node();
}
Insert cell
Insert cell
Insert cell
d3 = require('d3@6')
Insert cell
import {printTable} from '@uwdata/data-utilities'
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more