Обновление вложенной пузырьковой диаграммы d3 с потоковыми данными

Я пытался внедрить вложенную пузырьковую диаграмму для моего проекта, где мне нужно отображать, что происходит с разными клиентами в сети. Я использовал пример, показанный по адресу: https://bl.ocks.org/mbostock/7607535

Вот код, который я использую:

    /// <reference path="../bower_components/dt-d3/d3.d.ts" />
    var margin = 20, diameter = 960;
    var color = d3.scale.linear()
        .domain([-1, 4])
        .range(["#ff8080", "#b30000"])
        .interpolate(d3.interpolateHcl);
    var pack = d3.layout.pack()
        .padding(2)
        .size([diameter - margin, diameter - margin])
        .value(function (d) { return d.size; });
    var svg = d3.select("body").select("#parent").select("#svg1").append("svg")
    .attr("width", diameter)
    .attr("height", diameter)
  .append("g")
    .attr("transform", "translate(" + diameter / 2 + "," + diameter / 2 + ")");
    var svg2 = d3.select("body").select("#parent").select("#svg2").append("svg")
        .attr("width", 100)
        .attr("height", 100)
        .append("g")
        .attr("transform", "translate(" + margin + "," + margin + ")");
    function draw(fileToRead){
        console.log(fileToRead);
        d3.json(fileToRead, function (error, root) {
            if (error)
                throw error;
        var focus = root, nodes = pack.nodes(root), view;
        var circle = svg.selectAll("circle").data(nodes);
        circle.attr("class","update");
        circle.enter().append("circle")
            .attr("class", function (d) { return d.parent ? d.children ? "node" : "node node--leaf" : "node node--root"; })        .style("fill", function (d) { return d.children ? d.children.length > 3 ? color(d.depth) : "white" : d.size > 0.02 ? "#660000" : "#003366"; })
            .style("fill", function (d) { return d.children ? "white" : d.size > 0.02 ? "#660000" : "#003366"; })
            .on("click", function (d) { if (focus !== d)
            zoom(d), d3.event.stopPropagation(); });
        circle.exit().remove();
        var text = svg.selectAll("text").data(nodes);
        text.attr("class","update");
        text.enter().append("text")
            .attr("class", "label")
            .style("fill-opacity", function (d) { return d.parent === root ? 1 : 0; })
            .style("display", function (d) { return d.parent === root ? "inline" : "none"; })
            .style("fill", "black")
            .text(function (d) { return d.name; });
        text.exit().remove();
        var node = svg.selectAll("circle,text");
        d3.select("body")
            .style("background", color(-1))
            .on("click", function () { zoom(root); });
        zoomTo([root.x, root.y, root.r * 2 + margin]);
        function zoom(d) {
            var focus0 = focus;
            focus = d;
            if (focus.depth == 2) {
                circle2.style("fill", "white");
            } else {
                circle2.style("fill", "black");
            }
            var transition = d3.transition()
                .duration(d3.event.altKey ? 7500 : 750)
                .tween("zoom", function (d) {
                var i = d3.interpolateZoom(view, [focus.x, focus.y, focus.r * 2 + margin]);
                return function (t) { zoomTo(i(t)); };
            });
            transition.selectAll("text")
                .filter(function (d) { return d.parent === focus || this.style.display === "inline"; })
                .style("fill-opacity", function (d) { return d.parent === focus ? 1 : 0; })
                .each("start", function (d) { if (d.parent === focus)
                this.style.display = "inline"; })
                .each("end", function (d) { if (d.parent !== focus)
                this.style.display = "none"; });
        }
        function zoomTo(v) {
            var k = diameter / v[2];
            view = v;
            node.attr("transform", function (d) { return "translate(" + (d.x - v[0]) * k + "," + (d.y - v[1]) * k + ")"; });
            circle.attr("r", function (d) { return d.r * k; });
        }
        });
    }
    d3.select(self.frameElement).style("height", diameter + "px");

Проблема, с которой я сталкиваюсь, заключается в том, что я постоянно передаю новые данные в пользовательский интерфейс. Каждый раз я вызываю "Draw" с полным файлом JSON со всеми данными, которые у меня есть в базе данных. Я прочитал, что enter(), exit(), remove() отвечают за обновление элементов DOM, но, похоже, я что-то упустил. Когда вызывается функция draw(), кажется, что она рисует пузырьки над существующими, и такие вещи, как цвет рамки круга или цвет текста, исчезают. Мне интересно, правильно ли я использую команды enter () и exit (). Я хотел бы, чтобы функция draw() получала файл json и обновляла только информацию в пузырьковом графике, который является новым, и для него не существует элементов DOM, но, похоже, он просто воссоздает все элементы DOM снова.

Заранее спасибо, Георгий

PS: у меня есть два svg, потому что я пытаюсь разделить экран на две части с левой стороной, занятой пузырьковым графиком, и правой стороной с дополнительной информацией, отображаемой, когда мы увеличиваем его во внутреннем пузыре

EDIT1: Вот пример данных, которые я передаю в функцию draw():

{"name": "", 
    "children": 
        [{"name": "10.0.0.64", "children": 
            [{"name": "unidentified", "children": 
                [{"name": "datasource", "size": 0.002551020408163265}, {"name": "datasource", "size": 0.0031746031746031746}, {"name": "datasource", "size": 0.0017123779389460522}, {"name": "average", "size": 0.008550406895439414}, {"name": "datasource", "size": 0.019020266320109332}]}, 
            {"name": "www.quefaire.be", "children":                 
                [{"name": "average", "size": 0.002270490544565449}, {"name": "datasource", "size": 0.023243328100470965}]}, {"name": "tnsinternet.be", "children": [{"name": "average", "size": 0.002199967070440535}, {"name": "datasource", "size": 0.022009167303284966}]}, 
            {"name": "hy.sachinese.com", "children":                    
                [{"name": "average", "size": 0.0022121416372369493}, {"name": "datasource", "size": 0.022222222222222223}]}, {"name": "pagesdor.be", "children": [{"name": "average", "size": 0.0030411250824668935}, {"name": "datasource", "size": 0.03672943251374624}]}, 
            {"name": "r.254a.com", "children": 
                [{"name": "average", "size": 0.0018942905665264935}, {"name": "datasource", "size": 0.015873015873015872}]}, 
            {"name": "i.ctnsnet.com", "children": 
                [{"name": "average", "size": 0.0018942905665264935}, {"name": "datasource", "size": 0.015873015873015872}]}, 
            {"name": "link.carrefour.eu", "children": 
                [{"name": "average", "size": 0.0019793245801319357}, {"name": "datasource", "size": 0.017857142857142856}]}, 
            {"name": "goudengids.be", "children": 
                [{"name": "average", "size": 0.00457041828122788}, {"name": "datasource", "size": 0.06349206349206349}]}, {"name": "www.inmemoriam.be", "children": [{"name": "average", "size": 0.0018443560093702978}, {"name": "datasource", "size": 0.014707876206037973}]}, 
            {"name": "www1.gfk-wi.com", "children": 
                [{"name": "datasource", "size": 0.011986645572622365}, {"name": "average", "size": 0.0017277318393667718}]}, {"name": "bootstrapcdn.com", "children": [{"name": "average", "size": 0.008550406895439414}, {"name": "datasource", "size": 0.13314186424076532}]}, 
            {"name": "maxcdn.bootstrapcdn.com", "children": 
                [{"name": "datasource", "size": 0.031746031746031744}, {"name": "average", "size": 0.0027563593243117796}]}]}]}

Вот и мой CSS:

.svg-container {
    display: flex;
    position: relative;
    width: 100%;
    padding-bottom: 100%; /* aspect ratio */
    vertical-align: top;
    overflow: hidden;
}

.svg-content-responsive {
    display: inline-block;
    position: absolute;
    top: 10px;
    left: 0;
}

.container {
  float: left;
}

.node {
    cursor: pointer;
    stroke: darkgrey;
}

.node:hover {
    stroke: #000;
    stroke-width: 1.5px;
}

.node--leaf {
    fill: white;
}

.label {
    font: 11px "Helvetica Neue", Helvetica, Arial, sans-serif;
    text-anchor: middle;
    text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, -1px 0 0 #fff, 0 -1px 0 #fff;
}

.label,
.node--root,
.node--leaf {
    pointer-events: none;
}

1 ответ

Решение

Прежде всего убедитесь, что все узлы имеют уникальные имена или должны иметь некоторый идентификатор, чтобы однозначно определить узел. Используйте этот уникальный ключ, как показано ниже, чтобы однозначно идентифицировать данные. В вашем наборе данных мое решение не будет работать, потому что немногие узлы имеют неуникальные имена, такие как набор данных и т. Д.

Приведенное ниже решение будет работать только при наличии уникального имени.

Вместо

var circle = svg.selectAll("circle").data(nodes);

сделай это

var circle = svg.selectAll("circle").data(nodes, function(d){return d.name});

и вместо этого

var text = svg.selectAll("text").data(nodes);

сделай это

var text = svg.selectAll("text").data(nodes, function(d){return d.name});

exit() Функция не может работать должным образом из-за вышеизложенного.

Читайте о ключевой функции здесь

Другие вопросы по тегам