javascript - d3.js concentric ring chart - Stack Overflow

I am looking to develop an arc ring chart. Where each new set of data is displayed as a concentric ring

I am looking to develop an arc ring chart. Where each new set of data is displayed as a concentric ring inside the other.

/

I've tried to take a sum of the values in the data, and use this to help develop a proportion algorithm, to ensure each arc never does more than one cycle.

this part manages the concentric ring.

getArc: function(){
            var that = this;

            var radiusArray = [100, 80];

            function getRadiusRing(i){
                return that.radius-(i*20);              
            }
            var thickness = 15;

            var arc = d3.svg.arc()
                    .innerRadius(function(d){
                        return getRadiusRing(d.index);                      
                    })
                    .outerRadius(function(d){
                        return getRadiusRing(d.index)+thickness;    
                    })
                    .startAngle(function(d, i){
                        return d.startAngle;
                    })
                    .endAngle(function(d, i){
                        return d.endAngle;
                    });     
            return arc;
        }

but I think there is a flaw in the end angle calculations. If you enable more data - the concentric rings although they form correctly have suspicious arc lengths where they wrap around the entire chart?

setData: function(data){
            var diameter = 2 * Math.PI * this.radius;           
            var localData = new Array();

            var segmentValueSum = 0;
            $.each(data[0].segments, function( ri, va) {
                segmentValueSum+= va.value;
            });

            $.each(data[0].segments, function(ri, value) {

                var segmentValue = value.value;

                var fraction = segmentValue/segmentValueSum;

                var arcBatchLength = fraction*2*Math.PI;
                var arcPartition = arcBatchLength;      

                var startAngle = 0;
                var endAngle = ((ri+1)*arcPartition);               

                data[0].segments[ri]["startAngle"] = startAngle;
                data[0].segments[ri]["endAngle"] = endAngle;
                data[0].segments[ri]["index"] = ri;
            });

            localData.push(data[0].segments);

            return localData[0];        
        }

trying to build the chart to look like the following

I've enhanced the chart, but still have issues with the placement and updating of the legend, rings and values. Why is old data remaining? /

if there is more or less data - the labels don't get placed correctly

 //draw labels                      
            valueLabels = value_group.selectAll("text.value").data(reversedata)
            valueLabels.enter().append("svg:text")
            .attr("class", "value")
            .attr("transform", function(d) {
                var rings = counts;

                return "translate("+(that.radius+55)/rings+", 0)";
            })
             .attr("dx", function(d, i){
                return 19*i;            })
            .attr("dy", function(d, i){
                return -5;
            })
            .attr("text-anchor", function(d){
                return "start";
            }).text(function(d){
                return d.value;
            });

            valueLabels.transition().duration(300).attrTween("d", arcTween)
            valueLabels.exit().remove();   

I am looking to develop an arc ring chart. Where each new set of data is displayed as a concentric ring inside the other.

http://jsfiddle/NYEaX/101/

I've tried to take a sum of the values in the data, and use this to help develop a proportion algorithm, to ensure each arc never does more than one cycle.

this part manages the concentric ring.

getArc: function(){
            var that = this;

            var radiusArray = [100, 80];

            function getRadiusRing(i){
                return that.radius-(i*20);              
            }
            var thickness = 15;

            var arc = d3.svg.arc()
                    .innerRadius(function(d){
                        return getRadiusRing(d.index);                      
                    })
                    .outerRadius(function(d){
                        return getRadiusRing(d.index)+thickness;    
                    })
                    .startAngle(function(d, i){
                        return d.startAngle;
                    })
                    .endAngle(function(d, i){
                        return d.endAngle;
                    });     
            return arc;
        }

but I think there is a flaw in the end angle calculations. If you enable more data - the concentric rings although they form correctly have suspicious arc lengths where they wrap around the entire chart?

setData: function(data){
            var diameter = 2 * Math.PI * this.radius;           
            var localData = new Array();

            var segmentValueSum = 0;
            $.each(data[0].segments, function( ri, va) {
                segmentValueSum+= va.value;
            });

            $.each(data[0].segments, function(ri, value) {

                var segmentValue = value.value;

                var fraction = segmentValue/segmentValueSum;

                var arcBatchLength = fraction*2*Math.PI;
                var arcPartition = arcBatchLength;      

                var startAngle = 0;
                var endAngle = ((ri+1)*arcPartition);               

                data[0].segments[ri]["startAngle"] = startAngle;
                data[0].segments[ri]["endAngle"] = endAngle;
                data[0].segments[ri]["index"] = ri;
            });

            localData.push(data[0].segments);

            return localData[0];        
        }

trying to build the chart to look like the following

I've enhanced the chart, but still have issues with the placement and updating of the legend, rings and values. Why is old data remaining? http://jsfiddle/NYEaX/123/

if there is more or less data - the labels don't get placed correctly

 //draw labels                      
            valueLabels = value_group.selectAll("text.value").data(reversedata)
            valueLabels.enter().append("svg:text")
            .attr("class", "value")
            .attr("transform", function(d) {
                var rings = counts;

                return "translate("+(that.radius+55)/rings+", 0)";
            })
             .attr("dx", function(d, i){
                return 19*i;            })
            .attr("dy", function(d, i){
                return -5;
            })
            .attr("text-anchor", function(d){
                return "start";
            }).text(function(d){
                return d.value;
            });

            valueLabels.transition().duration(300).attrTween("d", arcTween)
            valueLabels.exit().remove();   
Share Improve this question edited Mar 5, 2014 at 18:31 Mick MacCallum 130k40 gold badges283 silver badges281 bronze badges asked Feb 14, 2014 at 12:26 The Old CountyThe Old County 13914 gold badges67 silver badges142 bronze badges 1
  • A related discussion: Concentric arcs using D3.js – blong Commented Jul 23, 2015 at 20:37
Add a ment  | 

3 Answers 3

Reset to default 2

Replace

    var endAngle = ((ri+1)*arcPartition); 

by

    var endAngle = startAngle + arcPartition; 

I've managed to fix all the bugs - Here is the final code as a jquery plugin, enjoy

http://jsfiddle/NYEaX/165/

$(document).ready(function() {


            (function( $ ){
                var methods = {
                    el: "",
                    init : function(options) {
                        var clone = jQuery.extend(true, {}, options["data"]);
                        var preparedData = methods.setData(clone);

                        methods.el = this;          
                        methods.setup(preparedData, options["width"], options["height"]);
                    },
                    setup: function(data, w, h){

                        var selector = methods.el["selector"];

                        var padding = 20;


                        var chart = d3.select(selector).append("svg:svg")
                            .attr("class", "chart")
                            .attr("width", w)
                            .attr("height", h)
                        .append("svg:g")
                            .attr("class", "concentricchart")
                            .attr("transform", "translate("+((w/3)+padding)+","+h/3+")");

                        methods.radius = Math.min(w, h) / 2;

                        var label_group = chart.append("svg:g")
                            .attr("class", "label_group")
                            .attr("transform", "translate("+((w/3)-15)+","+(-h/4)+")");

                        var legend_group = chart.append("svg:g")
                            .attr("class", "legend_group")
                            .attr("transform", "translate("+((w/3)-130)+","+((-h/4)-5)+")");

                        var value_group = chart.append("svg:g")
                            .attr("class", "value_group")
                            .attr("transform", "translate(0,"+(h/4)+")");

                        var path_group = chart.append("svg:g")
                            .attr("class", "path_group")
                            .attr("transform", "translate(0,"+(h/4)+")");               


                        this.generateArcs(selector, data);      
                    },
                    update: function(data){
                        var clone = jQuery.extend(true, {}, data);

                        var preparedData = methods.setData(clone);

                        methods.el = this;
                        methods.animate(preparedData);          
                        methods.oldData = preparedData;
                    },
                    animate: function(data){
                        var that = this;

                        var selector = methods.el["selector"];

                        that.generateArcs(selector, data);
                    },  
                    setData: function(data){
                            var diameter = 2 * Math.PI * this.radius;           
                            var localData = new Array();

                            var segmentValueSum = 0;
                            $.each(data[0].segments, function( ri, va) {
                                segmentValueSum+= va.value;
                            });

                            segmentValueSum = 200;//consistent total accross different data sets

                            $.each(data[0].segments, function(ri, value) {
                                var segmentValue = value.value;

                                var fraction = segmentValue/segmentValueSum;

                                var arcBatchLength = fraction*4*Math.PI;
                                var arcPartition = arcBatchLength;      

                                var startAngle = Math.PI/2;         
                                var endAngle = startAngle + arcPartition; 

                                data[0].segments[ri]["startAngle"] = startAngle;
                                data[0].segments[ri]["endAngle"] = endAngle;
                                data[0].segments[ri]["index"] = ri;
                            });

                            localData.push(data[0].segments);

                            return localData[0];        
                    },
                    textOffset: 10,
                    generateArcs: function(selector, data){
                        var that = this;

                        var chart = d3.select(selector);

                        //append previous value to it.          
                        $.each(data, function(index, value) {
                            if(that.oldData[index] != undefined){
                                data[index]["previousEndAngle"] = that.oldData[index].endAngle;
                            }
                            else{
                                data[index]["previousEndAngle"] = 0;
                            }
                        });     

                        var thickness = $(selector).data("thickness");
                        var ir = ($(selector).data("width")/3);


                        var path_group = d3.select(selector+ ' .path_group');

                        var arcpaths = path_group.selectAll("path")
                            .data(data);

                        arcpaths.enter().append("svg:path")
                            .attr("class", function(d, i){
                                return d.machineType;
                            })  
                            .style("fill", function(d, i){
                                return d.color;
                            })
                            .transition()
                            .ease("elastic")
                            .duration(750)
                            .attrTween("d", function(d){
                                 return that.arcTween(d, thickness, ir);
                            });      

                        arcpaths.transition()
                            .ease("elastic")
                            .style("fill", function(d, i){
                                return d.color;
                            })
                            .duration(750)
                            .attrTween("d", function(d){
                                 return that.arcTween(d, thickness, ir);
                            });      

                        arcpaths.exit().transition()
                            .ease("bounce")
                            .duration(750)
                            .attrTween("d", function(d){
                                 return that.arcTween(d, thickness, ir);
                            })   
                            .remove();

                        //draw labels       
                        that.drawLabels(chart, data, ir, thickness);
                        that.buildLegend(chart, data);
                    },
                    arcTween: function(b, thickness, ir){
                        var that = methods;

                        var prev = JSON.parse(JSON.stringify(b));
                        prev.endAngle = b.previousEndAngle;
                        var i = d3.interpolate(prev, b);

                        return function(t) {
                            return that.getArc(thickness, ir)(i(t));
                        };
                    },
                    drawLabels: function(chart, data, ir, thickness){
                        $(methods.el["selector"]+' .value_group').empty();

                        var that = this;

                        var reversedata = data.reverse();
                        var counts = data.length;

                        var value_group = d3.select(methods.el["selector"]+ ' .value_group');

                        valueLabels = value_group.selectAll("text.value").data(reversedata)
                        valueLabels.enter().append("svg:text")
                            .attr("class", "value")
                            .attr("transform", function(d) {       
                                return "translate("+(that.getRadiusRing(ir, counts-1))+", 0)";
                            })
                            .attr("dx", function(d, i){
                                return 20*i;            })
                            .attr("dy", function(d, i){
                                return -5;
                            })
                            .attr("text-anchor", function(d){
                                return "start";
                            }).text(function(d){
                                return d.value;
                            });

                        valueLabels
                            .transition()
                            .duration(300)
                            .attrTween("d", function(d){
                                return that.arcTween(d, thickness, ir);
                            })

                        valueLabels
                            .exit()
                            .remove();      
                    },
                    buildLegend: function(chart, data){
                        console.log("build legend");
                        $(methods.el["selector"]+' .label_group').empty();
                        $(methods.el["selector"]+' .legend_group').empty();


                        var label_group = d3.select(methods.el["selector"]+ ' .label_group');

                        //draw labels                       
                        labels = label_group.selectAll("text.labels")
                            .data(data.reverse());            

                        labels.enter().append("svg:text")
                            .attr("class", "labels")
                            .attr("dy", function(d, i){
                                return 19*i
                            })
                            .attr("text-anchor", function(d){
                                return "start";
                            })
                            .text(function(d){
                                return d.label;
                            });

                        labels.exit().remove();

                        var legend_group = d3.select(methods.el["selector"]+ ' .legend_group');

                        legend = legend_group.selectAll("circle").data(data);

                        legend.enter().append("svg:circle")
                            .attr("cx", 100)
                            .attr("cy", function(d, i){
                                return 19*i
                            })
                            .attr("r", 7)   
                            .attr("width", 18)
                            .attr("height", 18)
                            .style("fill", function(d){
                                return d.color;
                            });

                        legend.exit().remove();
                    },
                    getRadiusRing: function(ir, i){
                        return ir-(i*20);               
                    },
                    getArc: function(thickness, ir){
                        var that = this;

                        var arc = d3.svg.arc()
                            .innerRadius(function(d){
                                return that.getRadiusRing(ir, d.index);                     
                            })
                            .outerRadius(function(d){
                                return that.getRadiusRing(ir+thickness, d.index);   
                            })
                            .startAngle(function(d, i){
                                return d.startAngle;
                            })
                            .endAngle(function(d, i){
                                return d.endAngle;
                            });
                        return arc;
                    },
                    radius: 100,
                    oldData: ""
                };

                $.fn.concentric = function(methodOrOptions) {
                    if ( methods[methodOrOptions] ) {
                        return methods[ methodOrOptions ].apply( this, Array.prototype.slice.call( arguments, 1 ));
                    } else if ( typeof methodOrOptions === 'object' || ! methodOrOptions ) {
                        // Default to "init"
                        return methods.init.apply( this, arguments );
                    } else {
                        $.error( 'Method ' +  methodOrOptions + ' does not exist' );
                    }    
                };

            })(jQuery);




            var dataCharts = [
                {
                    "data": [
                        {
                            "segments": [
                                {
                                    "label": "Turkey",
                                    "value": 25,
                                    "color": "red"
                                },
                                {
                                    "label": "United States",
                                    "value": 40,
                                    "color": "blue"                         
                                },
                                {
                                    "label": "Switzerland",
                                    "value": 60,
                                    "color": "green"                            
                                },
                                {
                                    "label": "Iceland",
                                    "value": 80,
                                    "color": "gold"
                                }                           
                            ]
                        }
                    ]
                },
                {
                    "data": [
                        {
                            "segments": [
                                {
                                    "label": "Peanut Butter",
                                    "value": 50,
                                    "color": "red"
                                },
                                {
                                    "label": "Tea",
                                    "value": 25,
                                    "color": "orange"                           
                                },
                                {
                                    "label": "Cheese",
                                    "value": 25,
                                    "color": "purple"                           
                                }                       
                            ]
                        }
                    ]
                } ,
                {
                    "data": [
                        {
                            "segments": [
                                {
                                    "label": "Jam",
                                    "value": 90,
                                    "color": "purple"
                                },
                                {
                                    "label": "Lemons",
                                    "value": 15,
                                    "color": "brown"                            
                                }                       
                            ]
                        }
                    ]
                }           
            ];

            var clone = jQuery.extend(true, {}, dataCharts);

                //__invoke concentric
                $('[data-role="concentric"]').each(function(index) {
                    var selector = "concetric"+index;

                    $(this).attr("id", selector);

                    var options = {
                        data: clone[0].data,
                        width: $(this).data("width"),
                        height: $(this).data("height")
                    }

                    $("#"+selector).concentric(options);
                });


            $(".testers a").on( "click", function(e) {
                e.preventDefault();

                var clone = jQuery.extend(true, {}, dataCharts);

                var min = 0;
                var max = 2;

                //__invoke concentric
                $('[data-role="concentric"]').each(function(index) {
                    pos = Math.floor(Math.random() * (max - min + 1)) + min;
                    console.log("id", $(this).attr("id"));
                    $("#"+$(this).attr("id")).concentric('update', clone[pos].data);
                });

            }); 

});

I've made a version where the chart starts from the top - so different orientation. Be good to put this as a variable - maybe have the option to start the chart in 4 different quadrants.

http://jsfiddle/NYEaX/364/

I've adjusted

  • the start angle in set Data

    var startAngle = Math.PI*2;

-the label placements

valueLabels.enter().append("svg:text")
                            .attr("class", "value")
                            .attr("transform", function(d) {       
                                return "translate("+(that.getRadiusRing(ir, counts-1))+", 0)";
                            })
                            .attr("dx", function(d, i){
                                return 0;
                            })
                            .attr("dy", function(d, i){
                                return (thickness + 4)*i;
                            })
                            .attr("text-anchor", function(d){
                                return "start";
                            }).text(function(d){
                                return d.value;
                            });

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745474664a4629289.html

相关推荐

  • javascript - d3.js concentric ring chart - Stack Overflow

    I am looking to develop an arc ring chart. Where each new set of data is displayed as a concentric ring

    7小时前
    10

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信