Public
Edited
May 2
Insert cell
Insert cell
Insert cell
<svg width="1100" height="100">
<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 {}
.footprint {font-size: 17px;}
line {stroke: white; stroke-width: 2px;}
</style>

<text x="0" y="27" class="maintitle">Land Guzzlers: Part Two</text>
<text x="0" y="50" class="mainsubtitle">Structured Drawing</text>

<!-- the rest will be drawn by the data using D3 below -->
</svg>
Insert cell
Insert cell
Insert cell
{
let landguzzlersViz = d3.select(svgContainer)
// select the cell called svgContainer and make it a d3 object.
// draw Rectangles first - get this section to work before you try the others.
landguzzlersViz.selectAll("rect")
.data(land_guzzlers) // the name of the data object (see table below)
.join("rect")
.attr("width", d => how_do_we_calculate_the_width_from_d)
.attr("height", d => how_do_we_calculate_the_height_from_d)
.attr("x", what_value_should_these_all_be)
.attr("y", d => how_do_we_calculate_the_y_top_left_of_rectangle_position_from_d)
.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(land_guzzlers) // same dataset used again
// .join("text") // new object type created
// .attr("x", d => how_do_we_calculate_this_position)
// .attr("y", d => how_do_we_calculate_this_position)
// .text(d => what_data_field_is_this_coming_from) // this is the text contents, eg. LARGE DOG
// .attr("class", "title"); // tag it with the style class 'title' so the selectAll above works.

// // Now the rotated Eco-Footprint text and values. Note the rotation transform.
// landguzzlersViz.selectAll(".footprint") // selecting a .class of objects again.
// .data(land_guzzlers)
// .join("text")
// .attr("x", d => how_do_we_calculate_this_position)
// .attr("y", d => how_do_we_calculate_this_position)
// .text(d => "Concatenate text: " + d["somecolumname"] + " with a plus")
// .attr("transform", (d,i,nodes) => "rotate(90 " + nodes[i].getAttribute("x") + " " + nodes[i].getAttribute("y") + ")")
// // ^^ 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.
// .attr("class", "footprint"); // tag it with the style class 'footprint'

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

}
Insert cell
Insert cell
Insert cell
Insert cell
// Preparing the data

data = [
{ fname: "LARGE DOG", footprint: 1.11 },
{ fname: "MEDIUM-SIZED DOG", footprint: 0.84 },
{ fname: "TOYOTA LAND CRUISER", footprint: 0.41 },
{ fname: "VOLKSWAGEN GOLF", footprint: 0.32 },
{ fname: "CAT", footprint: 0.15 },
{ fname: "HAMSTER", footprint: 0.014 }
]
Insert cell
{
// 1. Setup
const width = 800;
const height = 600;
const margin = {top: 40, right: 40, bottom: 40, left: 40};
const colors = ["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#8c564b"];
// 2. Create SVG
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height]);

// 3. Grid layout
const gridCols = 3;
const cellWidth = (width - margin.left - margin.right) / gridCols;
const cellHeight = 200;

// 4. Create rectangles (scaled by footprint)
svg.selectAll("rect")
.data(data)
.join("rect")
.attr("x", (d, i) => margin.left + (i % gridCols) * cellWidth)
.attr("y", (d, i) => margin.top + Math.floor(i / gridCols) * cellHeight)
.attr("width", d => d.footprint * 150) // Adjust scale factor as needed
.attr("height", d => d.footprint * 150)
.attr("fill", (d, i) => colors[i])
.attr("stroke", "#333")
.attr("stroke-width", 1);

// 5. Add main labels
svg.selectAll(".title")
.data(data)
.join("text")
.attr("class", "title")
.attr("x", (d, i) => margin.left + (i % gridCols) * cellWidth + d.footprint * 150 - 10)
.attr("y", (d, i) => margin.top + Math.floor(i / gridCols) * cellHeight + 25)
.attr("text-anchor", "end")
.text(d => d.fname.toUpperCase())
.attr("font-size", "12px")
.attr("font-weight", "bold");

// 6. Add footprint values
svg.selectAll(".value")
.data(data)
.join("text")
.attr("class", "value")
.attr("x", (d, i) => margin.left + (i % gridCols) * cellWidth + d.footprint * 150 - 10)
.attr("y", (d, i) => margin.top + Math.floor(i / gridCols) * cellHeight + 45)
.attr("text-anchor", "end")
.text(d => `${d.footprint} hectares`)
.attr("font-size", "10px");

return svg.node();
}
Insert cell
{
// 1. Setup
const width = 800;
const height = 600;
const margin = {top: 40, right: 40, bottom: 40, left: 40};
const colors = ["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#8c564b"];
// 2. Create SVG
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height]);

// 3. Grid layout - matches hand-drawn structure
const positions = [
{x: 50, y: 50}, // Large Dog (top-left)
{x: 350, y: 50}, // Medium Dog (top-right)
{x: 50, y: 200}, // Land Cruiser (middle-left)
{x: 350, y: 200}, // Volkswagen (middle-right)
{x: 50, y: 350}, // Cat (bottom-left)
{x: 350, y: 350} // Hamster (bottom-right)
];

// 4. Create rectangles with manual positioning
svg.selectAll("rect")
.data(data)
.join("rect")
.attr("x", (d, i) => positions[i].x)
.attr("y", (d, i) => positions[i].y)
.attr("width", d => d.footprint * 200)
.attr("height", d => d.footprint * 200)
.attr("fill", (d, i) => colors[i])
.attr("stroke", "#333")
.attr("stroke-width", 1);

// 5. Add main labels (right-aligned in boxes)
svg.selectAll(".title")
.data(data)
.join("text")
.attr("class", "title")
.attr("x", (d, i) => positions[i].x + d.footprint * 200 - 10)
.attr("y", (d, i) => positions[i].y + 30)
.attr("text-anchor", "end")
.attr("font-size", "14px")
.attr("font-weight", "bold");

// 6. Add footprint values
svg.selectAll(".value")
.data(data)
.join("text")
.attr("class", "value")
.attr("x", (d, i) => positions[i].x + d.footprint * 200 - 10)
.attr("y", (d, i) => positions[i].y + 50)
.attr("text-anchor", "end")
.text(d => `${d.footprint.toFixed(2)} hectares`) // Ensures 2 decimal places
.attr("font-size", "12px");

return svg.node();
}
Insert cell
{
// Setup
const width = 800;
const height = 600;
const margin = {top: 40, right: 40, bottom: 40, left: 40};
const colors = d3.schemeTableau10;

// Create SVG with background rectangle
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height);
svg.append("rect")
.attr("width", width)
.attr("height", height)
.attr("fill", "#f8f8f8");

// Algorithmic layout
const padding = 5;
let x = margin.left;
let y = margin.top;
let rowHeight = 0;

// Process data sorted by footprint
const processedData = data.sort((a,b) => b.footprint - a.footprint)
.map(d => {
const size = Math.max(d.footprint * 150, 30); // Minimum 30px
const newX = x;
if (x + size > width - margin.right) {
x = margin.left;
y += rowHeight + padding;
rowHeight = 0;
}
rowHeight = Math.max(rowHeight, size);
x += size + padding;
return {...d, size, x: newX, y};
});

// Create unified rectangles
svg.selectAll("rect.item")
.data(processedData)
.join("rect")
.attr("class", "item")
.attr("x", d => d.x)
.attr("y", d => d.y)
.attr("width", d => d.size)
.attr("height", d => d.size)
.attr("fill", (d,i) => colors[i]);

// Add text labels
svg.selectAll("text.label")
.data(processedData)
.join("text")
.attr("class", "label")
.attr("x", d => d.x + d.size - 5)
.attr("y", d => d.y + 20)
.attr("text-anchor", "end")
.text(d => d.fname)
.style("font-size", d => Math.min(14, 8 + d.footprint * 4) + "px");

// Add values
svg.selectAll("text.value")
.data(processedData)
.join("text")
.attr("class", "value")
.attr("x", d => d.x + d.size - 5)
.attr("y", d => d.y + 35)
.attr("text-anchor", "end")
.text(d => d.footprint.toFixed(2) + " hectares")
.style("font-size", "10px");

return svg.node();
}
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
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