D3: диаграмма солнечных лучей с корневым узлом на внешнем кольце - PullRequest
0 голосов
/ 08 мая 2018

Я провожу некоторые исследования по оптимизации создания маркетинговой графики в моей компании.

Одна из них, в частности, диаграмма солнечных лучей, создается в Illustrator (сложно обновить), и я хочу переключить ее на графику D3 (ее легко обновить через файл JSON или CSV). Проблема в том, что диаграмма солнечных лучей находится в в обратном порядке ... корневой узел находится на внешнем кольце, а данные перетекают внутрь. Примечание: Я не могу поменять местами порядок данных, имеет таким образом.

Можно ли перевернуть порядок отрисовки диаграммы солнечных лучей в D3? Я экспериментировал с функцией сортировки ... она перевернула порядок сортировки корневой диаграммы дерева, как и ожидалось ... но, похоже, не влияет на порядок сортировки диаграммы солнечных лучей.

Это диаграмма солнечных лучей, с которой я работаю (с здесь ):

    // JSON data
    var nodeData = {
        "name": "TOPICS", "children": [{
            "name": "Topic A",
            "children": [{"name": "Sub A1", "size": 4}, {"name": "Sub A2", "size": 4}]
        }, {
            "name": "Topic B",
            "children": [{"name": "Sub B1", "size": 3}, {"name": "Sub B2", "size": 3}, {
                "name": "Sub B3", "size": 3}]
        }, {
            "name": "Topic C",
            "children": [{"name": "Sub A1", "size": 4}, {"name": "Sub A2", "size": 4}]
        }]
    };

    // Variables
    var width = 500;
    var height = 500;
    var radius = Math.min(width, height) / 2;
    var color = d3.scaleOrdinal(d3.schemeCategory20b);

    // Create primary <g> element
    var g = d3.select('svg')
        .attr('width', width)
        .attr('height', height)
        .append('g')
        .attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');

    // Data strucure
    var partition = d3.partition()
        .size([2 * Math.PI, radius]);

    // Find data root
    var root = d3.hierarchy(nodeData)
        .sum(function (d) { return d.size});

    // Size arcs
    partition(root);
    var arc = d3.arc()
        .startAngle(function (d) { return d.x0 })
        .endAngle(function (d) { return d.x1 })
        .innerRadius(function (d) { return d.y0 })
        .outerRadius(function (d) { return d.y1 });

    // Put it all together
    g.selectAll('path')
        .data(root.descendants())
        .enter().append('path')
        .attr("display", function (d) { return d.depth ? null : "none"; })
        .attr("d", arc)
        .style('stroke', '#fff')
        .style("fill", function (d) { return color((d.children ? d : d.parent).data.name); });
<script src="https://d3js.org/d3.v4.min.js"></script>
 <svg></svg>
 

Большое спасибо! Jb

1 Ответ

0 голосов
/ 09 мая 2018

Это должно быть довольно прямым, внутренний и внешний радиус каждой дуги необходимо изменить. В оригинале они установлены здесь:

var arc = d3.arc()
    .startAngle(function (d) { return d.x0 })
    .endAngle(function (d) { return d.x1 })
    .innerRadius(function (d) { return d.y0 })
    .outerRadius(function (d) { return d.y1 });

Вместо того, чтобы брать d.y0 и d.y1 в качестве внутреннего и внешнего радиусов, нам нужно взять эти значения и вычесть их из предельного радиуса солнечных лучей. Радиус определяется при масштабировании размера солнечных лучей:

var partition = d3.partition()
    .size([2 * Math.PI, radius]);

Итак, мы можем использовать:

var arc = d3.arc()
    .startAngle(function (d) { return d.x0 })
    .endAngle(function (d) { return d.x1 })
    .innerRadius(function (d) { return radius - d.y1 })
    .outerRadius(function (d) { return radius - d.y0 });

Это ставит родителей снаружи своих детей.

Я поменял местами d.y1 и d.y0, поскольку d.y0 относится к меньшему числу, это внутренний радиус при нормальном движении наружу, но с возвращенным значением, равным radius-d.y0, теперь используется для определения внешнего радиуса

Вот пример:

    // JSON data
    var nodeData = {
        "name": "TOPICS", "children": [{
            "name": "Topic A",
            "children": [{"name": "Sub A1", "size": 4}, {"name": "Sub A2", "size": 4}]
        }, {
            "name": "Topic B",
            "children": [{"name": "Sub B1", "size": 3}, {"name": "Sub B2", "size": 3}, {
                "name": "Sub B3", "size": 3}]
        }, {
            "name": "Topic C",
            "children": [{"name": "Sub A1", "size": 4}, {"name": "Sub A2", "size": 4}]
        }]
    };

    // Variables
    var width = 500;
    var height = 500;
    var radius = Math.min(width, height) / 2;
    var color = d3.scaleOrdinal(d3.schemeCategory20b);

    // Create primary <g> element
    var g = d3.select('svg')
        .attr('width', width)
        .attr('height', height)
        .append('g')
        .attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');

    // Data strucure
    var partition = d3.partition()
        .size([2 * Math.PI, radius]);

    // Find data root
    var root = d3.hierarchy(nodeData)
        .sum(function (d) { return d.size});

    // Size arcs
    partition(root);

    var arc = d3.arc()
        .startAngle(function (d) { return d.x0 })
        .endAngle(function (d) { return d.x1 })
        .innerRadius(function (d) { return radius - d.y1; })
        .outerRadius(function (d) { return radius - d.y0; });

    // Put it all together
    g.selectAll('path')
        .data(root.descendants())
        .enter().append('path')
        .attr("d", arc)
        .style('stroke', '#fff')
        .style("fill", function (d) { return color((d.children ? d : d.parent).data.name); });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
<svg></svg>

Обратите внимание, здесь я показываю корневой узел, в оригинале он не отображается (создается пончик). Если вы хотите сохранить эффект пончика, мы можем продолжать скрывать корень, но добавим некоторые отступы к значениям радиусов:

  // JSON data
    var nodeData = {
        "name": "TOPICS", "children": [{
            "name": "Topic A",
            "children": [{"name": "Sub A1", "size": 4}, {"name": "Sub A2", "size": 4}]
        }, {
            "name": "Topic B",
            "children": [{"name": "Sub B1", "size": 3}, {"name": "Sub B2", "size": 3}, {
                "name": "Sub B3", "size": 3}]
        }, {
            "name": "Topic C",
            "children": [{"name": "Sub A1", "size": 4}, {"name": "Sub A2", "size": 4}]
        }]
    };

    // Variables
    var width = 500;
    var height = 500;
    var radius = Math.min(width, height) / 2;
    var color = d3.scaleOrdinal(d3.schemeCategory20b);

    // Create primary <g> element
    var g = d3.select('svg')
        .attr('width', width)
        .attr('height', height)
        .append('g')
        .attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');

    // Data strucure
    var partition = d3.partition()
        .size([2 * Math.PI, radius]);

    // Find data root
    var root = d3.hierarchy(nodeData)
        .sum(function (d) { return d.size});

    // Size arcs
    partition(root);
    var arc = d3.arc()
        .startAngle(function (d) { return d.x0 })
        .endAngle(function (d) { return d.x1 })
        .innerRadius(function (d) { return radius - d.y1 + root.y1; })
        .outerRadius(function (d) { return radius - d.y0 + root.y1; });

    // Put it all together
    g.selectAll('path')
        .data(root.descendants())
        .enter().append('path')
        .attr("display", function (d) { return d.depth ? null : "none"; })
        .attr("d", arc)
        .style('stroke', '#fff')
        .style("fill", function (d) { return color((d.children ? d : d.parent).data.name); });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
<svg></svg>

Здесь я просто добавляю внешний радиус корневого узла (который является шириной дуги, поскольку внутренний радиус равен 0) к значениям радиусов. Обратите внимание, что если вы хотите сохранить пончик и показать корневой узел, вам необходимо соответствующим образом настроить размер диаграммы солнечных лучей. Хотя я более склонен называть это диаграммой солнечного взрыва.

...