D3.js - Graphs
D3.js - SVG Coordinate Space and Basic Charting
Section titled “D3.js - SVG Coordinate Space and Basic Charting”Understanding the SVG coordinate space is crucial for drawing with D3.js. While mathematical Cartesian coordinates typically have (0,0) at the bottom-left with Y increasing upwards, SVG’s coordinate system is different.
SVG Coordinate Space
Section titled “SVG Coordinate Space”The SVG coordinate space has two key characteristics:
- The origin (0,0) is at the top-left corner of the SVG viewport.
- The Y-coordinate increases downwards, while the X-coordinate increases to the right (similar to mathematical graphs).
So, if you draw a circle at cx=30, cy=30, it will be 30 units to the right and 30 units down from the top-left corner of the SVG element.
// Create an SVG container with a border to visualize its boundariesconst svgContainer = d3.select("body") .append("svg") .attr("width", 200) .attr("height", 200) .style("border", "1px solid black");
// A circle at (30,30) within this SVGsvgContainer.append("circle") .attr("cx", 30) .attr("cy", 30) .attr("r", 20) .style("fill", "steelblue");Example: Creating a Line Graph
Section titled “Example: Creating a Line Graph”A line graph is a common visualization that shows trends over time or a continuous variable. It typically has X and Y axes, with data points connected by lines.
Let’s create a line graph showing a fictional population growth. First, prepare a population-data.csv file:
year,population2006,402008,452010,482012,512014,532016,572017,62Here are the steps to draw the line graph:
Step 1: Setup HTML and Styles
Section titled “Step 1: Setup HTML and Styles”Create an HTML file and add basic styles for the line and axes.
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>D3 Line Graph</title> <script src="https://d3js.org/d3.v7.min.js"></script> <style> .line { fill: none; stroke: steelblue; stroke-width: 2px; } .axis path, .axis line { fill: none; stroke: #000; shape-rendering: crispEdges; } .axis text { font-family: sans-serif; font-size: 10px; } </style></head><body> <svg id="line-chart" width="600" height="400"></svg> <script> // D3 code will go here </script></body></html>Step 2: Define Margins and Dimensions
Section titled “Step 2: Define Margins and Dimensions”Use the margin convention to create space for axes.
const margin = {top: 20, right: 30, bottom: 40, left: 50};const width = 600 - margin.left - margin.right;const height = 400 - margin.top - margin.bottom;
const svg = d3.select("#line-chart") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", `translate(${margin.left},${margin.top})`);Step 3: Define Scales
Section titled “Step 3: Define Scales”Scales map data values to pixel positions. For ‘year’, we’ll use d3.scaleTime (assuming years are parsed as dates). For ‘population’, d3.scaleLinear.
const xScale = d3.scaleTime().range([0, width]);const yScale = d3.scaleLinear().range([height, 0]); // Note: 0 is at the bottom for y-axis mappingStep 4: Define the Line Generator
Section titled “Step 4: Define the Line Generator”d3.line() will generate the path string for our line.
const lineGenerator = d3.line() .x(d => xScale(d.year)) .y(d => yScale(d.population));Step 5: Load and Process Data
Section titled “Step 5: Load and Process Data”Load population-data.csv and parse the data types.
d3.csv("population-data.csv", d => { // Custom row accessor to parse data return { year: new Date(+d.year, 0, 1), // Convert year to a Date object (Jan 1st of that year) population: +d.population // Convert population to a number };}).then(data => { // Steps 6-9 will be inside this .then() block console.log(data);
// Step 6: Set Scale Domains xScale.domain(d3.extent(data, d => d.year)); yScale.domain([0, d3.max(data, d => d.population)]);
// Step 7: Draw the Line svg.append("path") .datum(data) .attr("class", "line") .attr("d", lineGenerator);
// Step 8: Add X-Axis svg.append("g") .attr("class", "axis x-axis") .attr("transform", `translate(0,${height})`) .call(d3.axisBottom(xScale).ticks(d3.timeYear.every(2)).tickFormat(d3.timeFormat("%Y")));
// X-axis Label svg.append("text") .attr("transform", `translate(${width / 2}, ${height + margin.bottom - 5})`) .style("text-anchor", "middle") .text("Year");
// Step 9: Add Y-Axis svg.append("g") .attr("class", "axis y-axis") .call(d3.axisLeft(yScale));
// Y-axis Label svg.append("text") .attr("transform", "rotate(-90)") .attr("y", 0 - margin.left) .attr("x", 0 - (height / 2)) .attr("dy", "1em") .style("text-anchor", "middle") .text("Population");
}).catch(error => { console.error("Error loading or processing data:", error);});Make sure population-data.csv is in the same directory as your HTML file, or adjust the path. When you open the HTML, you’ll see the line graph.
Key Concepts Used
Section titled “Key Concepts Used”- SVG Viewport & Grouping: Using
<svg>and<g>elements, and thetransformattribute. - Margins: The margin convention for chart layout.
- Scales (
d3-scale): Mapping data values to visual representations (pixels).d3.scaleTimefor time-based data,d3.scaleLinearfor quantitative data. - Axes (
d3-axis): Generating human-readable axis labels and ticks from scales. - Shapes (
d3-shape): Usingd3.line()to generate SVG path data from an array of points. - Data Loading (
d3-fetch): Asynchronously loading and parsing CSV data.
This example provides a foundation for building more complex charts. You can extend it with tooltips, interactivity, multiple lines, etc. For detailed information on SVG, refer to the MDN SVG Tutorial.