Подтвердить что ты не робот

D3 - Круговая диаграмма и наложенные на нее ярлыки

Я ищу создать круговую диаграмму с плавающей меткой, используя D3. Я новичок в D3, и я даже не уверен, что это возможно? Можете ли вы как-нибудь использовать метки одного графика в другом? Если можете, можете ли вы указать мне пример?

Сокращение Объяснение: Мне нужны ярлыки от: http://bl.ocks.org/1691430

enter image description here ... быть на круговой диаграмме.

Вот код, который я использовал ниже: Или в JSBIN: http://jsbin.com/awilak/1/edit

Если я правильно понимаю его код, это раздел, который добавляет метки. Я не понимаю, что делает labelForce.update. Оттуда меня не волнует переход, поэтому линия не нужна. Тогда остальное просто рисует круги и добавляет ссылку/строку? Если бы кто-то мог интегрировать это, это было бы потрясающе, но если вы можете помочь мне понять, что происходит и чего я не хватает, я был бы более чем благодарен.

// Now for the labels
// This is the only function call needed, the rest is just drawing the labels
anchors.call(labelForce.update)

labels = svg.selectAll(".labels")
    .data(data, function(d,i) {return i;})
labels.exit()
    .attr("class","exit")
    .transition()
    .delay(0)
    .duration(500)
    .style("opacity",0)
    .remove();

// Draw the labelbox, caption and the link
newLabels = labels.enter().append("g").attr("class","labels")

newLabelBox = newLabels.append("g").attr("class","labelbox")
newLabelBox.append("circle").attr("r",11)
newLabelBox.append("text").attr("class","labeltext").attr("y",6)
newLabels.append("line").attr("class","link")

labelBox = svg.selectAll(".labels").selectAll(".labelbox")
links = svg.selectAll(".link")
labelBox.selectAll("text").text(function(d) { return d.num})
}

<!DOCTYPE html>
<html>
<head>    
    <meta http-equiv="Content-type" content="text/html; charset=utf-8">
    <title>Testing Pie Chart</title>
    <script type="text/javascript" src="http://mbostock.github.com/d3/d3.js?2.1.3"></script>
    <script type="text/javascript" src="http://mbostock.github.com/d3/d3.geom.js?2.1.3"></script>
    <script type="text/javascript" src="http://mbostock.github.com/d3/d3.layout.js?2.1.3"></script>

    <style type="text/css">
    .slice text {
        font-size: 16pt;
        font-family: Arial;
    }   
    </style>
</head>
<body>
    <button id="button"> Test </button>
    <br>
    <form id="controls">
        <div>
            <h2>Y axis</h2>
            <ul id="y-axis">
                <li><label><input checked="checked" type="radio" name="y-axis" value="Component">Component</label></li>
                <li><label><input type="radio" name="y-axis" value="Browser">Browser</label></li>
                <li><label><input type="radio" name="y-axis" value="Version">Version</label></li>
            </ul>
        </div>
    </form>
    <script type="text/javascript">
    // return a list of types which are currently selected
    function plottableTypes () {
        var types = [].map.call (document.querySelectorAll ("#coaster-types input:checked"), function (checkbox) { return checkbox.value;} );
        return types;
     }


    var w = 600,                        //width
    h = 600,                            //height
    r = 100,
    r2 = 200,                           //radius
    axis = getAxis (),                  //axes
    color = d3.scale.category20c();     //builtin range of colors

    data = [
        {"Browser":"Internet Explorer ","Version":"8.0","Toatl":2000,"Component":"6077447412293130422"},
        {"Browser":"Internet Explorer ","Version":"9.0 ","Toatl":1852,"Component":"6077447412293130422"},
        {"Browser":"Internet Explorer ","Version":"6.0 ","Toatl":1754,"Component":"6077447412293130422"},
        {"Browser":"Firefox ","Version":"16.0 ","Toatl":1020,"Component":"6077447412293130422"},
        {"Browser":"Chrome ","Version":"23.0 ","Toatl":972,"Component":"6077447412293130422"},
        {"Browser":"Internet Explorer ","Version":"7.0 ","Toatl":700,"Component":"6077447412293130422"},
        {"Browser":"Mobile Safari ","Version":"6.0 ","Toatl":632,"Component":"6077447412293130422"},
        {"Browser":"BOT ","Version":"BOT ","Toatl":356,"Component":"6077447412293130422"},
        {"Browser":"Firefox ","Version":"8.0 ","Toatl":196,"Component":"6077447412293130422"},
        {"Browser":"Mobile Safari ","Version":"5.1 ","Toatl":184,"Component":"6077447412293130422"}
    ];

    var vis = d3.select("body")
        .append("svg:svg")              //create the SVG element inside the <body>
        .data([data])                   //associate our data with the document
        .attr("width", w)           //set the width and height of our visualization (these will be attributes of the <svg> tag
        .attr("height", h)
        .append("svg:g")                //make a group to hold our pie chart
        .attr("transform", "translate(" + r2 + "," + r2 + ")")    //move the center of the pie chart from 0, 0 to radius, radius

    var arc = d3.svg.arc()              //this will create <path> elements for us using arc data
        .outerRadius(r);


    var pie = d3.layout.pie()           //this will create arc data for us given a list of values
        .value(function(d) { return d.Toatl; });    //we must tell it out to access the value of each element in our data array

    var arcs = vis.selectAll("g.slice")     //this selects all <g> elements with class slice (there aren't any yet)
        .data(pie)                          //associate the generated pie data (an array of arcs, each having startAngle, endAngle and value properties) 
        .enter()                            //this will create <g> elements for every "extra" data element that should be associated with a selection. The result is creating a <g> for every object in the data array
        .append("svg:g")                //create a group to hold each slice (we will have a <path> and a <text> element associated with each slice)
        .attr("class", "slice");    //allow us to style things in the slices (like text)


    arcs.append("svg:path")
        .attr("fill", function(d, i) { return color(i); } ) //set the color for each slice to be chosen from the color function defined above
        .attr("d", arc);                                    //this creates the actual SVG path using the associated data (pie) with the arc drawing function


    arcs.append("svg:text")                                     //add a label to each slice
        .attr("transform", function(d) {                    //set the label origin to the center of the arc
            //we have to make sure to set these before calling arc.centroid
            d.innerRadius = r2;
            d.outerRadius = r;
            return "translate(" + arc.centroid(d) + ")";        //this gives us a pair of coordinates like [50, 50]
        })
        .attr("text-anchor", "middle")                          //center the text on it origin
        .text(function(d, i) { 
            if(axis.yAxis == "Component"){
                return data[i].Component;
            }
            return data[i].Browser;     //get the label from our original data array
        });       

        d3.select('#button').on('click', reColor);

        var arcOver = d3.svg.arc()
            .outerRadius(r + 30) 
            .innerRadius(0);
        var arc = d3.svg.arc()
            .outerRadius(r)
            .innerRadius(0); 

        var arcs = vis.selectAll("g.slice")
            .attr("class", "slice")
            .on("mouseover", function(d) {
                getAxis();
                d3.select(this)
                    .select("path")
                    .transition()
                    .duration(500)
                .attr("d", arcOver);
                d3.select(this).select("text")
                    .text(function(d, i) { 
                        if(axis.yAxis == "Component"){
                            return data[i].Component;
                        }
                    return data[i].Browser;     //get the label from our original data array
                });       
            })
            .on("mouseout", function(d) {
                getAxis();
                d3.select(this)
                    .select("path")
                    .transition()
                    .duration(500)
                    .attr("d", arc);
                d3.select(this)
                    .select("text")
                    .text(function(d, i) { 
                        if(axis.yAxis == "Component"){
                            return data[i].Component;
                        }
                        return data[i].Browser;     //get the label from our original data array
                    }); 
                });


        function reColor(){
            var slices = d3.select('body').selectAll('path');
            slices.transition()
                .duration(2000)
                .attr("fill", function(d, i) { return color(i+2); } );
            slices.transition()
                .delay(2000)
                .duration(2000)
                .attr("fill", function(d, i) { return color(i+10); } )
        }
        function makeData(){

        }
        // return an object containing the currently selected axis choices
        function getAxis () {
            var y = document.querySelector("#y-axis input:checked").value;
            return {
                yAxis: y,
            };
        }
        function update() {
            axis = getAxis()
            arcs.selectAll("text")          //add a label to each slice              
                .text(function(d, i) { 
                    if(axis.yAxis == "Component"){
                        return data[i].Component;
                    }
                    return data[i].Browser;     //get the label from our original data array
                });
            }

        document.getElementById("controls").addEventListener ("click", update, false);
        document.getElementById("controls").addEventListener ("keyup", update, false);
    </script>
</body>
</html>
4b9b3361

Ответ 1

Как упоминалось в комментариях к вашему вступительному сообщению, можно достичь решения, как вы его описали, и, возможно, используя свой код плюс части "движущихся меток" - пример. Если я правильно вас понимаю, вы хотите добиться неперекрывающихся ярлыков с помощью силового макета, что довольно хорошая идея, о которой я еще не наткнулся.

Кодовая часть, которую вы вставили из примера , просто рисует метки и строки, как вы уже правильно объяснили. Следующий шаг - переставить метки в силовом макете вокруг вашей круговой диаграммы.

Часть, которая переупорядочивает метки (и ссылки) в примере, следующая:

function redrawLabels() {
    labelBox
        .attr("transform",function(d) { return "translate("+d.labelPos.x+" "+d.labelPos.y+")"})

    links
        .attr("x1",function(d) { return d.anchorPos.x})
        .attr("y1",function(d) { return d.anchorPos.y})
        .attr("x2",function(d) { return d.labelPos.x})
        .attr("y2",function(d) { return d.labelPos.y})
}        

// Initialize the label-forces
labelForce = d3.force_labels()
    .linkDistance(0.0)
    .gravity(0)
    .nodes([]).links([])
    .charge(-60)
    .on("tick",redrawLabels)

Функция - это та, которая изменяет положение меток и линий. Сила вычисляется по D3 и начинается с d3.force_labels().... Как вы можете видеть, функция назначается как обработчик событий для события tick. Другими словами: после каждого шага вычисления силы D3 вызывает "ящик" для каждой метки и обновляет позиции.

К сожалению, я не очень хорошо знаком с методом force_labels() D3, но я бы предположил, что он работает почти как обычный force(). Якорь, в вашем случае, помещается где-то в каждом кусочке для каждой этикетки. Чем больше сосредоточено внутри каждого пирога (не сам пирог), тем лучше. К сожалению, вам нужно как-то вычислить эту позицию привязки (sin и cos stuff) и установить линии в это фиксированное положение в пределах redrawLabels().

После того, как вы это сделаете, вы увидите первый результат. Возможно, вам придется поиграть с гравитацией, ссылками и другими значениями силы для достижения хороших результатов. (Это то, что делают silders в примере).

См. d3 docs для получения дополнительной информации: https://github.com/mbostock/d3/wiki/Force-Layout

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

Идея также описана в следующем примере блоков: http://bl.ocks.org/mbostock/7881887

В этом примере узлы изначально помещаются в виртуальный круг. Позиционирование рассчитывается с помощью следующих функций:

x: Math.cos(i/m * 2 * Math.PI) * 200 + width/2 + Math.random(),

y: Math.sin(i/m * 2 * Math.PI) * 200 + height/2 + Math.random()

Они представляют круг с радиусом 200, место в центре панели чертежа. Круг делится на m одинаково больших частей. i/m просто вычисляет "кусочные позиции", где я изменяется от 0 до m-1.

Надеюсь, что смогу помочь!

Ответ 2

Да, вы можете скомбинировать силовые метки с круговой диаграммой! Нет ничего особенного в ярлыках круговых диаграмм, с которыми вы начали, это просто текстовые элементы, которые можно позиционировать, как и все остальное, с помощью transform или x/y. Похоже, что вы первоначально позиционировали эти метки в соответствии с центроидами дуг, которые они маркировали, но вы можете так же легко использовать другие критерии (например, вывод силовой компоновки).

D3 force layout вычисляет позиции для вещей, основанные на наборе ограничений относительно того, что фиксировано, что движимо и с которыми связано. Метод labelForce.update из примера Майка bl.ocks используется для информирования о силовом макете о том, сколько объектов нужно разместить, и где указаны фиксированные "якорные" точки. Затем он сохраняет вычисленные позиции для меток в модели данных диаграммы, и они используются позже в функции redrawLabels.

Ответ 3

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

// first arc used for drawing the pie chart
var arc = d3.svg.arc()
  .outerRadius(radius - 10)
  .innerRadius(0);

// label attached to first arc
g.append("text")
  .attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
  .attr("dy", ".35em")
  .style("text-anchor", "middle")
  .text(function(d) { return d.data.age; });

// second arc for labels
var arc2 = d3.svg.arc()
  .outerRadius(radius + 20)
  .innerRadius(radius + 20);

// label attached to second arc
g.append("text")
  .attr("transform", function(d) { return "translate(" + arc2.centroid(d) + ")"; })
  .attr("dy", ".35em")
  .style("text-anchor", "middle")
  .text(function(d) { return d.data.age; });