Иерархические данные в визуализации часто представляются в виде
вложенных структур, где каждый узел содержит дочерние элементы. Для
компактного отображения таких структур используется treemap-раскладка,
реализуемая в D3.js через d3.treemap().
Treemap преобразует дерево данных в набор прямоугольников, площадь которых пропорциональна значению узлов. Вся область визуализации последовательно делится между потомками, формируя плотную упаковку без пересечений.
Основа работы treemap — объект иерархии, создаваемый через
d3.hierarchy. Данные должны иметь структуру вложенных
объектов:
const data = {
name: "root",
children: [
{
name: "A",
value: 10
},
{
name: "B",
children: [
{ name: "B1", value: 5 },
{ name: "B2", value: 15 }
]
}
]
};
Промежуточные узлы могут не содержать value, если она
вычисляется из потомков.
Перед применением treemap данные преобразуются:
const root = d3.hierarchy(data)
.sum(d => d.value)
.sort((a, b) => b.value - a.value);
Метод sum определяет числовую нагрузку узлов,
используемую для вычисления площадей. Сортировка влияет на стабильность
и визуальную компактность размещения.
Функция d3.treemap() создаёт layout-алгоритм, который
настраивается параметрами:
const treemapLayout = d3.treemap()
.size([width, height])
.paddingInner(2)
.paddingOuter(4)
.paddingTop(10);
Основные параметры:
size([w, h]) — размер области отображенияpaddingInner — отступы между соседними листьямиpaddingOuter — отступы от границ контейнераpaddingTop — дополнительное пространство для
заголовковПосле настройки выполняется расчет координат:
treemapLayout(root);
После вызова каждому узлу добавляются свойства:
x0, y0 — верхний левый уголx1, y1 — нижний правый уголЭти координаты определяют прямоугольники, которые затем отображаются в SVG или Canvas.
Каждый узел иерархии становится прямоугольником:
root.leaves().forEach(d => {
console.log(d.data.name, d.x0, d.y0, d.x1, d.y1);
});
Площадь вычисляется пропорционально значению:
[ Area_{node} value_{node}]
Родительский узел представляет сумму всех потомков.
Типичная реализация использует группы <g> и
прямоугольники <rect>:
const svg = d3.select("svg")
.attr("width", width)
.attr("height", height);
const nodes = svg.selectAll("g")
.data(root.leaves())
.enter()
.append("g")
.attr("transform", d => `translate(${d.x0},${d.y0})`);
nodes.append("rect")
.attr("width", d => d.x1 - d.x0)
.attr("height", d => d.y1 - d.y0);
Каждый прямоугольник визуализирует листовой узел дерева.
Добавление текста требует учета размеров ячеек:
nodes.append("text")
.text(d => d.data.name)
.attr("x", 4)
.attr("y", 14)
.style("font-size", "12px");
Для предотвращения переполнения применяются проверки:
d3.treemap() использует алгоритм рекурсивного разбиения
пространства. Основная идея:
Различные стратегии укладки:
Алгоритм squarify минимизирует отклонение от квадратной формы, улучшая читаемость.
d3.treemap()
.tile(d3.treemapSquarify);
Или альтернативные варианты:
d3.treemapBinary
d3.treemapSlice
d3.treemapDice
d3.treemapSliceDice
Выбор влияет на визуальную структуру и восприятие плотности данных.
Каждый уровень иерархии может быть визуально различим:
nodes
.style("fill", d => d.depth === 1 ? "#ccc" : "#888");
Глубина узла определяется свойством depth, начиная с 0
для корня.
При изменении размера контейнера пересчёт выполняется повторно:
treemapLayout.size([newWidth, newHeight])(root);
После перерасчёта обновляются координаты всех узлов, включая вложенные уровни.
Treemap имеет структурные особенности:
valueЦвет часто отражает дополнительную метрику:
const color = d3.scaleOrdinal(d3.schemeCategory10);
nodes.select("rect")
.style("fill", d => color(d.parent.data.name));
Возможны схемы:
Если узел не содержит value, используется сумма
потомков:
d3.hierarchy(data)
.sum(d => d.value || 0);
Это обеспечивает автоматическую агрегацию вверх по дереву.
Перед визуализацией допускается ограничение глубины:
root.each(d => {
if (d.depth > 3) d.children = null;
});
Это уменьшает количество элементов и упрощает визуальную структуру.
Treemap чувствителен к количеству узлов. При больших деревьях применяются:
SVG остаётся предпочтительным при умеренных объёмах данных.
Каждый узел располагается в глобальной системе координат treemap:
x0, y0 — абсолютные координатыSVG-группы используют трансформацию:
.attr("transform", d => `translate(${d.x0},${d.y0})`);
Это позволяет отделить геометрию от содержимого узла.
При наличии ограниченного пространства текст адаптируется:
Интерактивные состояния часто зависят от событий:
nodes.on("mouseover", function(event, d) {
d3.select(this).style("opacity", 0.7);
});
Treemap относится к семейству иерархических layout-методов наряду с:
В отличие от них, treemap использует прямоугольную плотную упаковку, а не радиальную или линейную структуру.
При изменении данных выполняется полный пересчёт:
const updatedRoot = d3.hierarchy(newData)
.sum(d => d.value);
treemapLayout(updatedRoot);
После этого обновляются DOM-элементы через data join.
Treemap эффективен для представления:
Каждый уровень отражает вложенность, а площадь — вклад в общий объём.