| Lab 1 | Lab 2 → |
Successfully created a GitHub repository and added the starter files:
styles directory)scripts directory)Inspect the bar associated with the city Ottawa, is there any problems with it? What could be the reason?
There is a problem with the bar associated with the city Ottawa. The bar is not visible because the height of the bar.
The height for the SVG is set to 500px and the height of the bar is set to 700px.
This results in the bar being cut off at the top of the SVG.
main.js'use strict';
import BarChart from './barChart.js';
console.log(`d3.version: ${d3.version}`);
let cities = [
{city: 'Edinburgh', pop: 506000, area: 119, alt: 47},
{city: 'Dubai', pop: 3604000, area: 1610, alt: 5},
{city: 'Putrajaya', pop: 109000, area: 49, alt: 38},
{city: 'Qingdao', pop: 10071000, area: 11228, alt: 25},
{city: 'Lagos', pop: 8048000, area: 1171, alt: 41},
{city: 'Ottawa', pop: 1017000, area: 2790, alt: 70},
]
let bar1 = new BarChart('div#bar1', 800, 500);
// This line transforms the cities dataset in the generic format
// that BarChart expects: [[k,v], ...]
// we will explain it further in the next lab
let citiesElevation = cities.map(d => [d.city, d.alt]);
bar1.render(citiesElevation);
barChart.jsexport default class BarChart {
// Attributes (you can make those private too)
width; height; // size
svg; chart; bars; // selections
data; // internal data
// Constructor
constructor(container, width, height) {
this.width = width;
this.height = height;
this.svg = d3.select(container).append('svg')
.classed('barchart', true)
.attr('width', width).attr('height', height);
this.chart = this.svg.append('g');
this.bars = this.chart.selectAll('rect.bar');
}
// Private methods
// data is in the format [[key, value], ...]
#updateBars() {
this.bars = this.bars
.data(this.data, d => d[0])
.join('rect')
.classed('bar', true)
.attr('x', (d, i) => i*40+5)
.attr('y', d => this.height - d[1]*10)
.attr('width', 40)
.attr('height', d => d[1]*10);
}
// Public API
// The dataset parameter needs to be in a generic format,
// so that it works for all future data
// here we assume a [[k, v], ...] format for efficiency
render(dataset) {
this.data = dataset;
this.#updateBars();
return this; // to allow chaining
}
}
barchart.csssvg.barchart {
fill: #3F94D3;
stroke: #003C71;
stroke-width: 2px;
}
Includes extra attribute for margins in the Bar Chart constructor.
Creates a Scatter Plot that plots the population against the area.
Repurposes the code used in Bubble Charts and Bar Charts to include Scalability and Axes too.
main.js"use strict";
import BarChart from "./barChart_Tut3.js";
import BubbleChart from "./bubbleChart.js";
import ScatterPlot from "./scatterPlot.js";
// ...
/***** Bubble Chart *****/
let bubble1 = new BubbleChart("div#bubble1", 600, 400, [10, 40, 45, 20]);
let citiesArea = cities.map((d) => [d.city, d.alt]);
bubble1.render(citiesArea);
/***** Scatter Plot *****/
let scatter1 = new ScatterPlot("div#scatter1", 600, 400, [10, 50, 45, 20]);
let citiesPop = cities.map((d) => [d.pop, d.area]);
scatter1.render(citiesPop);
scatterPlot.jsexport default class ScatterPlot {
// Attributes (you can make those private too)
width; height; margin; // Size
svg; plot; scatters; // Selections
axisX; axisY; // Axes
labelX; labelY; // Labels
scaleX; scaleY; // Scales
data; // Internal Data
// Constructor
constructor(container, width, height, margin) {
this.width = width;
this.height = height;
this.margin = margin;
this.svg = d3.select(container).append("svg")
.classed("scatterplot", true)
.attr("width", width).attr("height", height);
this.plot = this.svg.append("g").attr("transform", `translate(${this.margin[2]}, ${this.margin[0]})`);
this.scatters = this.plot.selectAll("circle.scatter");
// Axes
this.axisX = this.svg.append("g")
.attr("transform", `translate(${this.margin[2]}, ${this.height - this.margin[1]})`);
this.axisY = this.svg.append("g").attr("transform", `translate(${this.margin[2]}, ${this.margin[0]})`);
// Labels
this.labelX = this.svg.append("text")
.attr("transform", `translate(${this.width / 2}, ${this.height})`)
.style("text-anchor", "middle").attr("dy", -5);
this.labelY = this.svg.append("text")
}
#updateScales() {
let plotWidth = this.width - this.margin[2] - this.margin[3],
plotHeight = this.height - this.margin[0] - this.margin[1];
let rangeX = [0, plotWidth],
rangeY = [plotHeight, 0];
let domainX = this.data.map((d) => d[0]),
domainY = [0, d3.max(this.data, (d) => d[1])];
this.scaleX = d3.scaleBand(domainX, rangeX).padding(0.2);
this.scaleY = d3.scaleLinear(domainY, rangeY).nice();
}
#updateAxes() {
let axisGenX = d3.axisBottom(this.scaleX),
axisGenY = d3.axisLeft(this.scaleY);
this.axisX.call(axisGenX);
this.axisY.call(axisGenY);
}
// Private methods
// data is in the format [[key, value], ...]
#updateScatter() {
this.scatters = this.scatters
.data(this.data, (d) => d[0])
.join("circle")
.classed("scatter", true)
.attr("cx", (d) => this.scaleX(d[0]) + this.scaleX.bandwidth() / 2)
.attr("cy", (d) => this.height - this.margin[1] - this.scaleY(d[1]))
.attr("r", 8);
}
// Public API
// The dataset parameter needs to be in a generic format,
// so that it works for all future data
// here we assume a [[k, v], ...] format for efficiency
render(dataset) {
this.data = dataset;
this.#updateScales();
this.#updateScatter();
this.#updateAxes();
return this; // to allow chaining
}
setLabels(labelX = "categories", labelY = "values") {
this.labelX.text(labelX);
this.labelY.text(labelY);
return this; // to allow chaining
}
}
scatterplot.csssvg.scatterplot {
fill: #3F94D3;
stroke: #003C71;
stroke-width: 2px;
border: 1px solid #121212;
}
text {
font-family: sans-serif;
font-size: 12px;
fill: #121212;
stroke: none;
}