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

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more