Table of contents
Setting attributes in SVG with D3
Adding & removing SVG elements with D3
foreword
This note is based on the video and pdf of the "Data Visualization" course taught by Zhang Shaokui of Tsinghua University in the spring of 2022.
Course link: Bilibili
Course github: github
1. Import D3.js
Import via script tag:
- Via internet link: https://d3js.org/d3.v5.min.js
- Via local file: ./d3.min.js
2. D3 Basic Grammar
Import SVG
<svg style='display: block; margin: 0 auto;'>
<g transform='translate(0,60)'>
<rect width=100 height=100 fill='#EEEEEE' />
<circle r=15 fill='#72bf67' cx=25 cy=30 />
<circle r=15 fill='rgb(100, 149, 237)' cx=75 cy=30 />
<g transform='translate(15,60) rotate(10)'>
<path d="M0,0 A40,40 10 0,0 65,0" fill='none' stroke='gray' stroke-width=5 />
</g>
</g>
</svg>
Querying SVGs with D3
d3.select(…)
•
d3.select(‘#rect1’)
•
Query elements with ID 'rect1'
•
# indicates that the following string is an ID
•
Find only one, if there is a duplicate name, only return the first one
d3.selectAll(…)
•
d3.selectAll(‘.class1’)
•
Query all elements whose class is 'class1'
•
d3.selectAll(‘rect’)
•
Query all elements whose tag is 'rect' (rect is the rectangle tag in SVG)
•
How many return how many
•
Can cooperate with Data-Join to select 'non-existent' primitives
• Add '#' before the ID, '.' before the Class, and no sign before the tag name.
Hierarchy based query :
•
d3.select(‘#maingroup rect’)
•
d3.selectAll('.tick text’)
•
d3.selectAll(‘#secondgroup rect’)
•
eg: '#secondgroup rect'
•
First find the tag whose id is secondgroup
•
Further find that the subtag of secondgroup is rect
•
It is still a query for rect, but the result is filtered by the parent tag
•
d3.select(…) can also be used to query categories, such as
•
d3.select(‘.class1’)
•
but will only return the first element found
• Therefore, it is recommended to use d3.selectAll for class and tag name queries
• It is recommended to use d3.select for the query of a specific element
Setting attributes in SVG with D3
common attributes
•
id, class (special attributes, can be set using .attr)
•
x, y, cx, cy (note the coordinate system of the screen! see next page)
•
fill, stroke
•
height, width, r (the radius of the circle)
•
transform -> translate, rotate, scale
The coordinate system of screen space is different from the common coordinate system
•
The upper left is the origin
•
Y and X are vertical down and horizontal to the right respectively
Set an attribute of an element: element.attr('attr_name', 'attr_value ')
•
Two parameters: attribute name, set value
•
rect1.attr(‘y’, ‘100’)
•
d3.select(‘#rect1’).attr(‘y’, ‘100’)
Get the attributes of an element: element.attr('attr_name')
•
One parameter: the attribute name
chain call
•
selection.attr(…).attr(…).attr(…)
•
.attr(…) returns the selected primitive itself
Modify an entire set of properties
DOM
•
Attributes of parent nodes affect child nodes
•
The attributes of the child node will be relative to the parent node
The following code can directly move all elements in the group
•
d3.select('#maingroup’)
•
.attr('transform', 'translate(200, 100)')
Adding & removing SVG elements with D3
element.append(…)
•
const myRect = svg.append(‘rect’);
•
const myRect = d3.select(‘#mainsvg’).append(‘rect’)
•
const myRect = d3.select(‘#mainsvg’).append(‘rect’).attr(‘x’, ‘100’)
D3's chained addition (call)
•
const myRect = d3.select(‘#mainsvg’).append(‘g’).attr(‘id’, ‘maingroup’)
•
.append(‘rect’).attr(‘fill’, ‘yellow’)
element.remove()
•
Use with care
•
will remove the entire tab
Tip: In the process of debugging, you can consider using the 'opacity' attribute to hack the removal effect
•
element.attr(‘opacity’, ‘0’)
Data reading – CSV data
d3.csv(…):
•
Read a certain CSV file under the target path.
•
e.g., d3.csv(‘static/data/hello.csv’);
d3.csv is a JavaScript asynchronous function :
•
It is not possible to obtain its return value directly
, such as:
•
let myData = d3.csv(‘static/data/hello.csv’);
❌
d3.csv('path/to/data.csv').then(
data
=> { // 'code logic after reading data' } )
•
The read data
should be obtained by means of .then(
data => {…}).
• '
data => {...}'
in then(...) is a
function .
•
The input ( parameter ) accepted by
this
function , namely
data , is the read data.
d3.csv('test.cvs').then(data => {
console.log(data);
});
JavaScript asynchronous mechanism (not required below):
•
d3.csv is an asynchronous function, even if no data is read, the following code will continue to execute.
•
After d3.csv is called, its return value is a JavaScript 'Promise' object (object).
•
Promise 'ask': what
to do
? '
What to do
' corresponds to the content of
the function
in .then() .
Numerical calculation of D3.js
d3.max(array)
•
Returns the maximum value in an array.
•
e.g., d3.max([5,4,6,1,8,16,9]) // 16
d3.min(array)
•
Returns the minimum value in an array.
d3.min
([5,4,6,1,8,16,9]) // 1
d3.extent(array)
•
Return the minimum value and maximum value at the same time, in the form of an array, that is, [minimum value, maximum value].
•
d3.extent([5,4,6,1,8,16,9]) // [1, 16]
The contents of the array can be any object:
•
Each object may contain multiple properties.
•
Which attribute to take as the maximum value will prompt d3.max, d3.min and d3.extent through the callback function.
eg
,
•
let a = [
{name: 'Shao-Kui', age:25, height: 176}, {name:'Wen-Yang', age:24, height: 180},
{name:'Liang Yuan', age: 29, height: 172}, {name:'Wei-Yu', age:23, height: 173}]
•
d3.max(a, d => d.age) // 29
•
d3.max(a, d => d.height) // 180
•
d3.extent(a, d => d.height) // [172, 180]
•
d3.min(a, d => d.age) // 23
scale
•
The scale is used to map the actual
data space
to
the screen (canvas) space
, that is, the conversion of the two spaces.
•
Commonly used to map data and create axes.
•
The difference lies mainly in the scale of the data.
Scale - Linear
d3.scaleLinear():
•
Define a linear scale and return a
function
.
•
eg, let scale = d3.scaleLinear(); // scale is a function
scale.domain([min_d, max_d]).range([min, max]):
•
Set
the definition domain
and
value range
of the scale .
•
Both the definition domain and the value range of the linear scale are continuous (Continuous), and the maximum and minimum values need to be given respectively.
•
e.g.,
const scale = d3.scaleLinear().domain([20, 80]).range([0, 120]);
Scale is essentially a function
:
•
scale(20) // 0
•
scale(50) // 60
It is often used in conjunction with the read data and interfaces such as d3.max:
•
const xScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)])
.range([0, innerWidth]);
Scale - Band
d3.scaleBand():
•
Define a 'strip' scale, returned as a
function
.
•
e.g., let scale = d3.scaleBand();
scale.domain(array).range([min, max]):
•
Set the definition domain and value range of the scale.
•
The definition domain of the Band scale is discrete (Discrete), and the value domain is continuous.
•
e.g.,
const scale = d3.scaleBand().domain([‘a’, ‘b’, ‘c’]).range([0, 120]);
Scale is essentially a function :
•
scale('b') // 40
•
scale(‘c') // 80
scale.padding(0.1):
•
Set the spacing of the strips as a percentage of their respective regions.
scale.bandwidth():
•
Returns the length of the strip.
d3.csv('platform_globalsale.csv').then(data => {
// calculationg scales:
yScale.domain(data.map(yValue)).range([0, innerHeight]).padding(0.1);
xScale.domain([0, d3.max(data, xValue)]).range([0, innerWidth]);
// data-join for rectangles:
mainGroup.selectAll('rect').data(data).join('rect')
.attr('height', yScale.bandwidth()).attr('width', d => xScale(xValue(d)))
.attr('x', 0).attr('y', d => yScale(yValue(d)));
// adding axes:
const xAxisMethod = d3.axisBottom(xScale);
const yAxisMethod = d3.axisLeft(yScale);
const xAxisGroup = mainGroup.append('g').call(xAxisMethod);
const yAxisGroup = mainGroup.append('g').call(yAxisMethod);
xAxisGroup.attr('transform', `translate(${0}, ${innerHeight})`);
})
to be continued. . .