Published
Edited
Feb 13, 2022
Insert cell
Insert cell
data =
[{x: 10, y: 20, c:"A"},
{x: 20, y: 20, c:"A"},
{x: 20, y: 10, c:"A"},
{x: 10, y: 10, c:"A"},
{x: 10, y: 20, c:"A"},
{x: 30, y: 40, c:"B"},
{x: 20, y: 20, c:"B"},
{x: 40, y: 20, c:"B"},
{x: 30, y: 40, c:"B"}
]
Insert cell
Insert cell
scatterplot(data, "x", "y", "c")
Insert cell
Insert cell
Insert cell
Insert cell
linkscat = (dataset, grouped_data, title, var_x, var_y, var_color, W, x_legend = var_x, y_legend) => {
// Parametrages /////////////////////////////////////////////////////////////////
if(W<500)
W = 500
const H = W/2 + W/10
const x_max_r = W-(W/5)
const y_max_r = H-(H/6)
const margin = {start: 25, bottom: 25, top: 50, end:50}
const y_range = [margin.top, y_max_r]
const x_range = [margin.start*2, x_max_r]
const translate = {x:H-margin.bottom,y:margin.start*2}
const rayon_circles = 5;
// Element racine ///////////////////////////////////////////////////////////////
const svg = d3.create("svg").attr("width", W).attr("height", H)

// Insertions
const g = svg.append("g")
///////////////////////////////////////////////////////////////////////////////////
// Mises a l'echelle /////////////////////////////////////////////////////////
let yMax = d3.max(dataset, d => d[var_y])
let xMax = d3.max(dataset, d => d[var_x])
const x = d3.scaleLinear()
.domain([0, xMax])
.range(x_range)
const y = d3.scaleLinear()
.domain([yMax, 0])
.range(y_range)
// couleurs
const c = d3.scaleOrdinal(d3.schemeCategory10)
.domain(grouped_data.map(d=>d[0]))
/////////////////////////////////////////////////////////////////////////////
// Cercles et Lignes ////////////////////////////////////////////////////////////
let ligne = d3.line()
.x(d => x(d[var_x]))
.y(d => y(d[var_y]))

svg.selectAll("ligne").data(grouped_data)
.enter()
.append("path")
.attr("class", "line")
.attr("fill", "none")
.attr("stroke", d => c(d[0]))
.attr("d", d => ligne(d[1]))

g.selectAll("circle").data(dataset)
.enter()
.append("circle")
.attr("cx", d => x(d[var_x]))
.attr("cy", d => y(d[var_y]))
.attr("r", rayon_circles)
.attr("fill", d => c(d[var_color]))
///////////////////////////////////////////////////////////////////////////////////
// Legendes ///////////////////////////////////////////////////////////////////////
// Titres X et Y
svg.append("text")
.attr("text-anchor", "middle")
.attr("alignment-baseline", "middle")
.attr("font-weight", "bold")
.attr("font-size", W/70)
.attr("x", W/2 - margin.start)
.attr("y", y_max_r+margin.bottom)
.text(x_legend)

const titreY = svg.append("g").attr("transform", `rotate(-90,0,0)`)
titreY.append("text")
.attr("text-anchor", "middle")
.attr("alignment-baseline", "middle")
.attr("font-weight", "bold")
.attr("font-size", W/70)
.attr("x", -translate.x/2)
.attr("y", margin.start/2)
.text(y_legend)
// Ajout du titre
svg.append("text")
.attr("text-anchor", "middle")
.attr("alignment-baseline", "middle")
.attr("stroke", "#3F3F3F")
.attr("x", W/2 - margin.start)
.attr("y", H-margin.bottom*0.75)
.attr("font-size", W/55)
.attr("font-style", "italic")
.attr("font-weight", "lighter")
.text(title)
// Clef ////////////////////////////////////////////////////////////////////////

const groups = Array.from(d3.map(grouped_data, d => d))
g.selectAll("text.clefs").data(groups)
.enter()
.append("text")
.attr("class", "clefs")
.attr("font-size", W/70)
.attr("x", x_max_r + margin.end)
.attr("y", (d, i) =>margin.top + i * 20 + 8)
.text(d => d[0])
g.selectAll("rect.clefs").data(groups)
.enter()
.append("rect")
.attr("x", x_max_r + (margin.end/1.5))
.attr("y", (d, i) => margin.top + i * 20 )
.attr("width", 10)
.attr("height", 10)
.attr("fill", d => c(d[0]))
.attr("stroke", "black")
// Brush //////////////////////////////////////////////////////////////
svg.append("style").text(`
.point--selected {
fill: red;
fill-opacity: 1;
stroke: red;
stroke-width: 5px;
}
`);
svg.append("g")
.attr("class", "brush")
.call(d3.brush().on("brush", brushed));
// Axes /////////////////////////////////////////////////////////////////////////
const xAxe = g => g.attr("transform", `translate(0, ${y_max_r})`)
.call(d3.axisBottom(x))
g.append("g").call(xAxe);
const yAxe = g => g.attr("transform", `translate(${translate.y}, 0)`)
.call(d3.axisLeft(y))
g.append("g").call(yAxe);

return svg.node()
}
Insert cell
linkscat(data, data_grouped, "Titre", "x", "y", "c", 800, "x", "y")
Insert cell
Insert cell
data_derived = data.map((d, i) => {
let r = {}
// A compléter
r.x = d.x
r.y = d.y
r.c = d.c
r.next = (i < data.length) ? data[i+1] : 'false';
return r
})
Insert cell
## 2- Canvas
### 2.1/ En utilisant d3.line
Insert cell
draw_canvas = (width, height) => {
let ctx = DOM.context2d(width, height);
ctx.beginPath(); // Start a new path
d3.line().
ctx.stroke(); // Render the path
return ctx.canvas;
}

Insert cell
### 2.2/ En utilisant lineTo()
Insert cell
## 2- Tables
Insert cell
columns = Object.keys(data[0])
Insert cell
table = {
let columns = Object.keys(data[0])
console.log(Object.entries(data))
const table = d3.create("table")
table.append("thead")
.join("tr")
.selectAll("th")
.data(columns)
.join("th")
.text(d => d)
.style("background-color", "gray")
.style("color", "white")
table.append("tbody")
.selectAll("tr")
.data(data)
.join("tr")
.style("background-color", (d, i) => i%2 ? "lightgray" : "white")
.selectAll("td")
.data(d,i => Object.entries(data[i]))
.join("td")
.text(d => d.value);

return table.node()
}
Insert cell
html`<style>
table {
border: 1px solid black;
border-collapse: collapse;
}
th, td {
padding: 1em;
}
</style>`
Insert cell
## 3 - Brushing
Insert cell
height = 200
Insert cell
brush_ft = (dataset, grouped_data, title, var_x, var_y, var_color, W, x_legend = var_x, y_legend) => {
// Parametrages /////////////////////////////////////////////////////////////////
if(W<500)
W = 500
const H = W/2 + W/10
const margin = {start: 25, end:50, bottom: 25, top: 50}
const x_max_r = W-margin.end
const y_max_r = H-margin.bottom
const y_range = [margin.top, y_max_r]
const x_range = [margin.start*2, x_max_r]
const translate = {x:H-margin.bottom,y:margin.start*2}
const rayon_circles = 5;
let yMax = d3.max(dataset, d => d[var_y])
let xMax = d3.max(dataset, d => d[var_x])
// Element racine ///////////////////////////////////////////////////////////////
const svg = d3.create("svg").attr("width", W).attr("height", H)

const g = svg.append("g")
///////////////////////////////////////////////////////////////////////////////////
// Mises a l'echelle /////////////////////////////////////////////////////////
const x = d3.scaleLinear()
.domain([0, xMax])
.range(x_range)
const y = d3.scaleLinear()
.domain([yMax, 0])
.range(y_range)
// couleurs
const c = d3.scaleOrdinal(d3.schemeCategory10)
.domain(grouped_data.map(d=>d[0]))

svg.append("style").text(`
.autour {
fill: blue;
fill-opacity: 1;
stroke: blue;
stroke-width: 3px;
}
.dans {
fill: lightgreen;
fill-opacity: 1;
stroke: lightgreen;
stroke-width: 5px;
}`);
// Clef ////////////////////////////////////////////////////////////////////////
const groups = Array.from(d3.map(grouped_data, d => d))
g.selectAll("text.clefs").data(groups)
.enter()
.append("text")
.attr("class", "clefs")
.attr("font-size", W/70)
.attr("x", x_max_r + margin.end)
.attr("y", (d, i) =>margin.top + i * 20 + 8)
.text(d => d[0])
g.selectAll("rect.clefs").data(groups)
.enter()
.append("rect")
.attr("x", x_max_r + (margin.end/1.5))
.attr("y", (d, i) => margin.top + i * 20 )
.attr("width", 10)
.attr("height", 10)
.attr("fill", d => c(d[0]))
.attr("stroke", "black")
// Axes /////////////////////////////////////////////////////////////////////////
const xAxe = g => g.attr("transform", `translate(0, ${y_max_r})`)
.call(d3.axisBottom(x))
g.append("g").call(xAxe);
const yAxe = g => g.attr("transform", `translate(${translate.y}, 0)`)
.call(d3.axisLeft(y))
g.append("g").call(yAxe);
/////////////////////////////////////////////////////////////////////////////
// Cercles et Lignes ///////////////////////////////////////////////////////
let ligne = d3.line()
.x(d => x(d[var_x]))
.y(d => y(d[var_y]))

svg.selectAll("ligne").data(grouped_data)
.enter()
.append("path")
.attr("class", "line")
.attr("fill", "none")
.attr("stroke", d => c(d[0]))
.attr("d", d => ligne(d[1]))

const circle = g.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", d => x(d[var_x]))
.attr("cy", d => y(d[var_y]))
.attr("r", rayon_circles)
.attr("fill", d => c(d[var_color]))
// Brush //////////////////////////////////////////////////////////////
const brush = d3.brush()
.on("brush", brushed);
svg.append("g")
.attr("class", "brush")
.call(brush)

function brushed({selection}) {
if (selection === null) {
circle.attr("fill", d => c(d[var_color]));
circle.attr("fill-opacity", 0.9);
} else {
// const [[x0, y0], [x1, y1]] = selection;
// selection.map( sel_lims =>{
// let x_data = x.invert(sel_lims[0])
// let y_data = y.invert(sel_lims[1])
// console.log("x= "+x_data+", y= "+y_data)
// })
// circle.attr("fill", d => { return get_selected(selection, [d.x, d.y]) ? "red" : "black"

// // const [dx, dy] = [x.invert(d.x + x_max_r), y.invert(d.y)]
// })
circle.attr("fill", selection && (d => contains(selection, [x(d.x), y(d.y)]) ? "red" : "lightgray"));
circle.attr("fill-opacity", selection && (d => contains(selection, [x(d.x), y(d.y)]) ? 0.9 : 0.6));
}
}
return svg.node()
}
Insert cell
function contains([[x0, y0], [x1, y1]], [x, y]) {
return x >= x0 && x < x1 && y >= y0 && y < y1;
}
Insert cell
brush_ft(data, data_grouped, "Titre", "x", "y", "c", 800, "x", "y")
Insert cell
Insert cell
import {scatterplot} from "@zidane-dev/tp2"
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