Public
Edited
Apr 13
Insert cell
Insert cell
data = FileAttachment("land_guzzlers.csv").csv();
Insert cell
data_no_ham = [data[0], data[0], data[1], data[2], data[3], data[4]]
Insert cell
data_ham = [data[5]]
Insert cell
Insert cell
<svg width="1100" height="1100">
<style>
/* edit these styles as you need - this is a starting point*/
rect {stroke: white; stroke-width: 2px;}
text {fill: white; font-family: 'Rajdhani', sans-serif;}
.maintitle {font-size: 39px; fill: black;}
.mainsubtitle {font-size: 18px; fill: black;}
.title {text-anchor: end; font-size: 32px;}
.subtext1, .subtext2, .subtext3 {text-anchor: end; font-size: 15px}
.footprint {text-anchor: start; font-size: 12px;}
.underline1 {stroke: white; stroke-width: 2px;}
.underline2 {stroke: white; stroke-width: 2px;}
line {stroke: white; stroke-width: 2px;}
</style>

<text x="0" y="27" class="maintitle">Land Guzzlers</text>
<text x="0" y="50" class="mainsubtitle">The ecological footprints of our pets can make SUVs look positively eco-friendly</text>

<!-- the rest will be drawn by the data using D3 below -->
</svg>
Insert cell
Insert cell
Insert cell
{
let landguzzlersViz = d3.select(svgContainer);
let padding = 20;
let scaleFactor = 800;

data.sort((a, b) => b["footprint"] - a["footprint"]);
let cumulativeWidth = 0;
let cumulativeHeight = 0;
// draw Rectangles first - get this section to work before you try the others.
landguzzlersViz.selectAll("rect")
.data(data) // the name of the data object (see table below)
.join("rect")
.attr("width", d => Math.sqrt(d["footprint"]) * scaleFactor)
.attr("height", d => Math.sqrt(d["footprint"]) * scaleFactor)
.attr("x", 0)
.attr("y", d => 1100 - Math.sqrt(d["footprint"]) * scaleFactor)
.style("fill", (d,i) => colors[i]);
// I'll give you this one. Can you see how it works? See colors array below.


// uncomment lines below as you go to make them live, and edit.
// Command-slash(/) or Control-slash(/) on a line or selection of lines will comment / uncomment.
// // Now add the Large Dog, Toyota, etc. titles
// // notice that I'm selectingAll objects of a class called .title to only get those text elements.
landguzzlersViz.selectAll(".title")
.data(data_no_ham) // same dataset used again
.join("text") // new object type created
.attr("x", d => Math.sqrt(d["footprint"]) * scaleFactor - (5 * Math.sqrt(d["footprint"])))
.attr("y", d => 1100 + 25 - Math.sqrt(d["footprint"]) * scaleFactor)
.text(d => d.title) // this is the text contents, eg. LARGE DOG
.attr("class", "title")
.attr("text-anchor", "end"); // tag it with the style class 'title' so the selectAll above works.
// // Now the rotated Eco-Footprint text and values. Note the rotation transform.
// selecting a .class of objects again.

landguzzlersViz.select(".title")
.data(data_ham) // same dataset used again
.join("text") // new object type created
.attr("x", d => Math.sqrt(d["footprint"]) * scaleFactor + 125)
.attr("y", d => 1100 + 21 - Math.sqrt(d["footprint"]) * scaleFactor)
.text(d => d.title) // this is the text contents, eg. LARGE DOG
.attr("class", "title")
.attr("text-anchor", "end"); // tag it with the style class 'title' so the selectAll above works.
// // Now the rotated Eco-Footprint text and values. Note the rotation transform.

// Add rotated text (Eco-footprint)
landguzzlersViz.selectAll(".footprint")
.data(data)
.join("text")
.attr("class", "footprint")
.attr("x", d => {
if (d.title === "HAMSTER") {
// Horizontal position for HAMSTER text
return ((Math.sqrt(d["footprint"]) * scaleFactor / 2) + 50); // Centered horizontally
} else {
// Default position for other entries
return Math.sqrt(d["footprint"]) * scaleFactor - 15; // Adjusted for rotation
}
})
.attr("y", d => {
if (d.title === "HAMSTER") {
// Vertical position below the title
return 1100 - Math.sqrt(d["footprint"]) * scaleFactor + 35; // Below the rectangle
} else {
// Default position for other entries
return 1100 - Math.sqrt(d["footprint"]) * scaleFactor + 15;
}
})
.text(d => `Eco-footprint: ${d["footprint"]} hectares`)
.attr("transform", d => {
if (d.title !== "HAMSTER") {
// Apply rotation for non-HAMSTER entries
const x = Math.sqrt(d["footprint"]) * scaleFactor - 20;
const y = 1100 - Math.sqrt(d["footprint"]) * scaleFactor + 25;
return `rotate(90, ${x}, ${y})`;
}
// No rotation for HAMSTER
return null;
});
// // ^^ OK, so you would not have gotten this one on your own easily. See if you can figure it out.
// // transform is "rotate(angle xPivotPoint yPivotPoint)" This is an SVG transform.
// tag it with the style class 'footprint'

// ... what other objects do we need and how do we get them. Use the same approaches.

//subtitle 1
landguzzlersViz.selectAll(".subtitle")
.data(data)
.join("text")
.attr("class", "titles")
.attr("x", d => Math.sqrt(d["footprint"]) * scaleFactor - (25 * Math.sqrt(d["footprint"])))
.attr("y", d => 1100 + 50 - Math.sqrt(d["footprint"]) * scaleFactor)
.text(d => d.subtitle)
.attr("text-anchor", "end");


//subtitle 2
landguzzlersViz.selectAll(".subtitle")
.data(data)
.join("text")
.attr("class", "titles")
.attr("x", d => Math.sqrt(d["footprint"]) * scaleFactor - (25 * Math.sqrt(d["footprint"])))
.attr("y", d => 1100 + 65 - Math.sqrt(d["footprint"]) * scaleFactor)
.text(d => d.subtitle2)
.attr("text-anchor", "end");
//subtitle 3
landguzzlersViz.selectAll(".subtitle")
.data(data)
.join("text")
.attr("class", "titles")
.attr("x", d => Math.sqrt(d["footprint"]) * scaleFactor - (25 * Math.sqrt(d["footprint"])))
.attr("y", d => 1100 + 80 - Math.sqrt(d["footprint"]) * scaleFactor)
.text(d => d.subtitle3)
.attr("text-anchor", "end");


// Draw underline1
landguzzlersViz.selectAll(".underline1")
.data(land_guzzlers.filter(d => d.footprint === 0.84 || d.footprint === 0.41))
.join("line")
.attr("x1", d => Math.sqrt(d["footprint"]) * scaleFactor - 300) // Adjusted offset
.attr("y1", d => 1100 - Math.sqrt(d["footprint"]) * scaleFactor + 32) // Align with rectangle bottom
.attr("x2", d => Math.sqrt(d["footprint"]) * scaleFactor - 15) // Adjusted offset
.attr("y2", d => 1100 - Math.sqrt(d["footprint"]) * scaleFactor + 32) // Align with rectangle bottom
.attr("class", "underline1");

// Draw underline2
landguzzlersViz.selectAll(".underline2")
.data(land_guzzlers.filter(d => d.footprint === 0.84 || d.footprint === 0.41))
.join("line")
.attr("x1", d => Math.sqrt(d["footprint"]) * scaleFactor - 15) // Adjusted offset
.attr("y1", d => 1100 - Math.sqrt(d["footprint"]) * scaleFactor + 32) // Align with rectangle bottom
.attr("x2", d => Math.sqrt(d["footprint"]) * scaleFactor - 15) // Adjusted offset
.attr("y2", d => 1100 - Math.sqrt(d["footprint"]) * scaleFactor + 100) // Extend downward
.attr("class", "underline2");
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
colors = ["#a5620b","#c29657","#cc1f5e","#a71949","#f7991d","#231f20"];
// in order Large Dog to Hamster. Reference as colors[i]
Insert cell
Insert cell
land_guzzlers.csv
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
land_guzzlers_data = FileAttachment("land_guzzlers.csv").csv().then(data => {
data.forEach(d => {
d["footprint"] = +d["footprint"];
});
return data;
});
Insert cell
Insert cell
<link href="https://fonts.googleapis.com/css?family=Rajdhani" rel="stylesheet">
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