如何在D3.js中更改beeswarm图中点的大小

时间:2017-03-06 17:16:58

标签: d3.js visualization

我一直在关注d3.js中一个令人讨厌的情节的this example,我正在试图弄清楚如何改变圆点的大小而不让圆圈重叠。似乎点的半径发生变化,在运行计算放置点的位置时,不会考虑这一点。

1 个答案:

答案 0 :(得分:3)

这是一个很酷的可视化。

我在这里填写了一句:https://plnkr.co/edit/VwyXfbc94oXp6kXQ7JFx?p=preview并将其修改为更像你正在寻找的工作(我认为)。真正的关键是改变处理碰撞的调用以根据圆的半径而变化(在原始帖子中它被硬编码为4,这在r === 3时效果很好但在r增长时失败)。变化:

  • 将圆半径变为变量(script.js的第7行,var r = 3;
  • 更改d3.forceCollide来电以使用该半径和乘数 - 第110行(.force("collide", d3.forceCollide(r * 1.333))
  • 更改.enter()来电以使用该半径(第130行:.attr("r", r)

这对r的合理值非常有效 - 但是您需要调整高度,甚至可以更改整个事情,以便r基于在height上(例如var r = height * .01)。您现在注意到,圆圈离开图表区域的底部和顶部。

这篇文章也许有趣:Conflict between d3.forceCollide() and d3.forceX/Y() with high strength() value

以下是script.js的后代:

var w = 1000, h = 280;

var padding = [0, 40, 34, 40];
var r = 5;

var xScale = d3.scaleLinear()
    .range([ padding[3], w - padding[1] ]);

var xAxis = d3.axisBottom(xScale)
    .ticks(10, ".0s")
    .tickSizeOuter(0);

var colors = d3.scaleOrdinal()
    .domain(["asia", "africa", "northAmerica", "europe", "southAmerica", "oceania"])
    .range(['#e41a1c','#377eb8','#4daf4a','#984ea3','#ff7f00','#ffff33']);

d3.select("#africaColor").style("color", colors("africa"));
d3.select("#namericaColor").style("color", colors("northAmerica"));
d3.select("#samericaColor").style("color", colors("southAmerica"));
d3.select("#asiaColor").style("color", colors("asia"));
d3.select("#europeColor").style("color", colors("europe"));
d3.select("#oceaniaColor").style("color", colors("oceania"));

var formatNumber = d3.format(",");

var tt = d3.select("#svganchor").append("div")  
    .attr("class", "tooltip")               
    .style("opacity", 0);

var svg = d3.select("#svganchor")
    .append("svg")
    .attr("width", w)
    .attr("height", h);

var xline = svg.append("line")
    .attr("stroke", "gray")
    .attr("stroke-dasharray", "1,2");

var chartState = {};

chartState.variable = "totalEmission";
chartState.scale = "scaleLinear";
chartState.legend = "Total emissions, in kilotonnes";

d3.csv("co2bee.csv", function(error, data) {
    if (error) throw error;

    var dataSet = data;

    xScale.domain(d3.extent(data, function(d) { return +d.totalEmission; }));

    svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + (h - padding[2]) + ")")
        .call(xAxis);

    var legend = svg.append("text")
        .attr("text-anchor", "middle")
        .attr("x", w / 2)
        .attr("y", h - 4)
        .attr("font-family", "PT Sans")
        .attr("font-size", 12)
        .attr("fill", "darkslategray")
        .attr("fill-opacity", 1)
        .attr("class", "legend");

    redraw(chartState.variable);

    d3.selectAll(".button1").on("click", function(){
        var thisClicked = this.value;
        chartState.variable = thisClicked;
        if (thisClicked == "totalEmission"){
            chartState.legend = "Total emissions, in kilotonnes";
        }
        if (thisClicked == "emissionPerCap"){
            chartState.legend = "Per Capita emissions, in metric tons";
        }
        redraw(chartState.variable);
    });

    d3.selectAll(".button2").on("click", function(){
        var thisClicked = this.value;
        chartState.scale = thisClicked;
        redraw(chartState.variable);
    });

    d3.selectAll("input").on("change", filter);

    function redraw(variable){

        if (chartState.scale == "scaleLinear"){ xScale = d3.scaleLinear().range([ padding[3], w - padding[1] ]);}

        if (chartState.scale == "scaleLog"){ xScale = d3.scaleLog().range([ padding[3], w - padding[1] ]);}

        xScale.domain(d3.extent(dataSet, function(d) { return +d[variable]; }));

        var xAxis = d3.axisBottom(xScale)
            .ticks(10, ".0s")
            .tickSizeOuter(0);

        d3.transition(svg).select(".x.axis").transition().duration(1000)
            .call(xAxis);

        var simulation = d3.forceSimulation(dataSet)
            .force("x", d3.forceX(function(d) { return xScale(+d[variable]); }).strength(2))
            .force("y", d3.forceY((h / 2)-padding[2]/2))
            .force("collide", d3.forceCollide(r * 1.333))
            .stop();

        for (var i = 0; i < dataSet.length; ++i) simulation.tick();

        var countriesCircles = svg.selectAll(".countries")
            .data(dataSet, function(d) { return d.countryCode});

        countriesCircles.exit()
            .transition()
            .duration(1000)
            .attr("cx", 0)
            .attr("cy", (h / 2)-padding[2]/2)
            .remove();

        countriesCircles.enter()
            .append("circle")
            .attr("class", "countries")
            .attr("cx", 0)
            .attr("cy", (h / 2)-padding[2]/2)
            .attr("r", r)
            .attr("fill", function(d){ return colors(d.continent)})
            .merge(countriesCircles)
            .transition()
            .duration(2000)
            .attr("cx", function(d) { console.log(d); return d.x; })
            .attr("cy", function(d) { return d.y; });

        legend.text(chartState.legend);

        d3.selectAll(".countries").on("mousemove", function(d) {
            tt.html("Country: <strong>" + d.countryName + "</strong><br>"
            + chartState.legend.slice(0, chartState.legend.indexOf(",")) + ": <strong>" + formatNumber(d[variable]) + "</strong>" + chartState.legend.slice(chartState.legend.lastIndexOf(" ")))
                .style('top', d3.event.pageY - 12 + 'px')
                .style('left', d3.event.pageX + 25 + 'px')
                .style("opacity", 0.9);

                xline.attr("x1", d3.select(this).attr("cx"))
                    .attr("y1", d3.select(this).attr("cy"))
                    .attr("y2", (h - padding[2]))
                    .attr("x2",  d3.select(this).attr("cx"))
                    .attr("opacity", 1);

        }).on("mouseout", function(d) {
            tt.style("opacity", 0);
            xline.attr("opacity", 0);
        });

        d3.selectAll(".x.axis, .legend").on("mousemove", function(){
            tt.html("This axis uses SI prefixes:<br>m: 10<sup>-3</sup><br>k: 10<sup>3</sup><br>M: 10<sup>6</sup>")
                .style('top', d3.event.pageY - 12 + 'px')
                .style('left', d3.event.pageX + 25 + 'px')
                .style("opacity", 0.9);
        }).on("mouseout", function(d) {
            tt.style("opacity", 0);
        });

    //end of redraw
    }

    function filter(){

        function getCheckedBoxes(chkboxName) {
          var checkboxes = document.getElementsByName(chkboxName);
          var checkboxesChecked = [];
          for (var i=0; i<checkboxes.length; i++) {
             if (checkboxes[i].checked) {
                checkboxesChecked.push(checkboxes[i].defaultValue);
             }
          }
          return checkboxesChecked.length > 0 ? checkboxesChecked : null;
        }

        var checkedBoxes = getCheckedBoxes("continent");

        var newData = [];

        if (checkedBoxes == null){ 
            dataSet = newData; 
            redraw(); 
            return;
        };

        for (var i = 0; i < checkedBoxes.length; i++){
            var newArray = data.filter(function(d){
                return d.continent == checkedBoxes[i];
            });
            Array.prototype.push.apply(newData, newArray);
        }

        dataSet = newData;

        redraw(chartState.variable);

    //end of filter
    }

//end of d3.csv
});