Graphiques et visualisation de données

Un des objectifs fondamentaux du Web est de présenter et échanger de l’information. Cette information peut être textuelle, visuelle, spatiale, temporelle ou interactive.

Les représentations graphiques peuvent être :

Une visualisation dynamique transforme des données en structures graphiques qui peuvent être explorées, filtrées et manipulées.

Ces graphiques peuvent être générés :


Le pipeline de la visualisation

Toutes les bibliothèques modernes de visualisation suivent le même schéma conceptuel :

Données → Transformation → Mise en page → Encodage → Rendu → Interaction
Étape Rôle
Données CSV, JSON, API, bases
Transformation filtrer, agréger, normaliser
Mise en page calculer les positions
Encodage couleur, taille, forme
Rendu SVG, Canvas, WebGL
Interaction zoom, survol, sélection

D3, Vega, Deck.gl, Tableau, PowerBI implémentent tous ce pipeline.


Les modèles de rendu du Web

Le navigateur propose trois modèles graphiques :

Modèle Technologie Principe
Vectoriel DOM SVG chaque forme est un nœud DOM
Bitmap Canvas un buffer de pixels
GPU WebGL rendu via shaders

Chaque modèle implique :


Dessin 2D avec Canvas

<canvas> fournit un buffer de pixels.

Une fois qu’un objet est dessiné, il n’existe plus que sous forme de pixels.

var canvas = document.getElementById("c");
var ctx = canvas.getContext("2d");

ctx.fillStyle = "red";
ctx.fillRect(10, 10, 100, 100);

Canvas est :

Utilisé pour :

Librairies :


Rendu GPU et 3D avec WebGL

WebGL permet d’utiliser la carte graphique via JavaScript.

C’est :

On utilise rarement WebGL directement. On passe par :

Librairie Usage
Three.js scènes 3D
Deck.gl grandes visualisations
Babylon.js moteurs 3D
Regl WebGL bas niveau

WebGL devient indispensable pour :


SVG — Graphismes vectoriels dans le DOM

SVG est un langage XML intégré au DOM.

Chaque forme est un élément DOM :

<circle cx="50" cy="50" r="20" fill="red"/>

SVG offre :

SVG est idéal pour :

Il devient lent au-delà de quelques milliers d’éléments.


D3.js — Data-Driven Documents

D3 relie des données au DOM.

Il ne dessine pas directement : il calcule, projette et synchronise.

D3 fournit :

Le rendu est fait par :

Usage moderne :

D3 calcule, un autre moteur dessine.


La jointure de données

svg.selectAll("circle")
  .data(data)
  .join("circle")
  .attr("cx", d => x(d.x))
  .attr("cy", d => y(d.y))
  .attr("r", 5);

Cela synchronise le DOM avec les données.


Visualisation déclarative avec Vega

Vega décrit des graphiques via JSON.

{
  "mark": "bar",
  "encoding": {
    "x": {"field": "année"},
    "y": {"aggregate": "sum", "field": "ventes"}
  }
}
D3 Vega
impératif déclaratif
code description
très flexible plus contraint

Performances et passage à l’échelle

Nombre d’éléments Technologie
< 1 000 SVG
1 000 – 100 000 Canvas
> 100 000 WebGL

Le DOM est coûteux. Les pixels sont rapides. Le GPU est imbattable.


D3 et React

React gère l’état. D3 gère les calculs.

Architecture typique :

État React → D3 (scales, layout) → SVG ou Canvas

Ne jamais laisser React et D3 modifier les mêmes nœuds DOM.

Librairies modernes :


Stack moderne de visualisation

En 2025, une application typique :

Couche Outils
Données APIs, Arrow, DuckDB
Transformation D3, Arquero
Mise en page D3, Vega
Rendu SVG, Canvas, WebGL
UI React
Haut niveau Vega, Deck.gl, Observable

Exemple react + d3 + svg

Sans jamais laisser D3 toucher au DOM.

import { useMemo, useState } from "react";
import * as d3 from "d3";

/**
 * === 0. RAW DATA =====================================
 * Données brutes, telles qu’elles arrivent depuis l’API.
 * Aucune sémantique graphique ici.
 */
const data = [
  { region: "IDF", value: 120, volume: 3000 },
  { region: "IDF", value: 80,  volume: 2000 },
  { region: "NAQ", value: 150, volume: 5000 },
  { region: "PAC", value: 60,  volume: 1000 },
  { region: "NAQ", value: 110, volume: 4200 }
];

export default function ScatterPlot() {

  /**
   * === 1. INTERACTION / STATE ==========================
   * L’utilisateur agit sur l’interface → cela modifie l’état React.
   * Cet état est l’entrée du pipeline.
   */
  const [region, setRegion] = useState(null);

  const width = 500;
  const height = 300;

  /**
   * === 2. DATA TRANSFORM ==============================
   * Sélection, filtrage, agrégation.
   * On produit une table de données "logiques"
   * prête à être encodée graphiquement.
   */
  const filteredData = useMemo(() => {
    return region
      ? data.filter(d => d.region === region)
      : data;
  }, [region]);

  /**
   * === 3. LAYOUT (D3) =================================
   * On calcule comment les données vont être projetées
   * dans l’espace graphique (échelles).
   * D3 joue ici le rôle de moteur mathématique.
   */
  const xScale = useMemo(() => {
    return d3.scaleLinear()
      .domain(d3.extent(filteredData, d => d.value))
      .range([40, width - 20]);
  }, [filteredData]);

  const yScale = useMemo(() => {
    return d3.scaleLinear()
      .domain(d3.extent(filteredData, d => d.volume))
      .range([height - 30, 20]);
  }, [filteredData]);

  const colorScale = d3.scaleOrdinal()
    .domain(["IDF", "NAQ", "PAC"])
    .range(["red", "blue", "green"]);

  /**
   * === 4. ENCODING ===================================
   * On transforme chaque ligne de données
   * en primitives graphiques abstraites.
   * (positions, tailles, couleurs)
   */
  const visualMarks = filteredData.map(d => ({
    cx: xScale(d.value),
    cy: yScale(d.volume),
    r: 6,
    fill: colorScale(d.region)
  }));

  /**
   * === 5. RENDERING ==================================
   * React + SVG produisent l’image finale.
   * D3 ne touche jamais le DOM.
   */
  return (
    <div>

      {/* === INTERACTION ============================== */}
      <select onChange={e => setRegion(e.target.value || null)}>
        <option value="">Toutes régions</option>
        <option value="IDF">IDF</option>
        <option value="NAQ">NAQ</option>
        <option value="PAC">PAC</option>
      </select>

      <svg width={width} height={height}>

        {/* Axes (rendu pur SVG) */}
        <line x1="40" y1="20" x2="40" y2={height - 30} stroke="#999" />
        <line x1="40" y1={height - 30} x2={width - 20} y2={height - 30} stroke="#999" />

        {/* Marques visuelles (résultat du pipeline) */}
        {visualMarks.map((m, i) => (
          <circle
            key={i}
            cx={m.cx}
            cy={m.cy}
            r={m.r}
            fill={m.fill}
          />
        ))}

      </svg>
    </div>
  );
}