Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
779 views
in Technique[技术] by (71.8m points)

d3.js - Custom legend based on color of bars in a dc.js composite chart

I implemented a composite chart with two bar charts in which one bar chart consists of bars with different colored bars. Now, I want to create a custom legend that represents each color bar (similar to https://dc-js.github.io/dc.js/examples/pie-external-labels.html used for pie chart). Below is the code snippet of what I've done so far:

var buttonPress = dc.barChart(composite)
    .dimension(joyTimeDimension)
    //.renderlet(colorRenderlet)
    //.colors('red')
    .colors(colorbrewer.Set1[5])
    .colorDomain([101, 105])
    .colorAccessor(function (d) {
        return d.value;
    })
    .group(btnGroup, "Button Press")
    .keyAccessor(function(d) {return d.key[0];})
    .valueAccessor(function (d) {
        return d.value;
    })
    .title( function(d){ 
        return [
            "Time: "+d.key[0],
            "button Name: "+d.key[1],
            "button: "+ d.value
        ].join('
')
    });


var joyStick = dc.barChart(composite)
    .dimension(joyTimeDimension)
    .colors('blue')
    .group(stepperGroup,"Joy Stick Movement")
    .keyAccessor(function(d) {return d.key[0];})
    .title( function(d){ 
        return [
            "Time: "+d.key[0],
            "Stepper Position: "+ d.value
        ].join('
')
  });
  composite
    .width(1200)
    .transitionDuration(500)
    .margins({top: 30, right: 50, bottom: 25, left: 40})
    .x(d3.time.scale().domain([startDate,currDate]))
    .xUnits(function(){return 150;})
    //.xUnits(d3.time.second)
    .elasticY(true)
    .legend(dc.legend().x(1000).y(4).itemHeight(13).gap(5))
    .renderHorizontalGridLines(true)
    .renderTitle(true)
    .shareTitle(false)
    .compose([buttonPress, joyStick])
    .brushOn(false)

Composite chart looks like this

Is there a way to create a custom legend for this scenario? Thanks in advance.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Let me provide a little bit of background about how the legend is built.

The legend in dc.js is really not all that sophisticated. It just calls .legendables() on the chart, and the chart decides what items to display in the legend.

Each chart has its own special-purpose code for this.

If we look at the source for compositeChart.legendables(), it's just recursively getting the legendables for each child chart and concatenating them:

_chart.legendables = function () {
    return _children.reduce(function (items, child) {
        if (_shareColors) {
            child.colors(_chart.colors());
        }
        items.push.apply(items, child.legendables());
        return items;
    }, []);
};

The pie chart creates a legendable for each pie slice:

_chart.legendables = function () {
    return _chart.data().map(function (d, i) {
        var legendable = {name: d.key, data: d.value, others: d.others, chart: _chart};
        legendable.color = _chart.getColor(d, i);
        return legendable;
    });
};

The legendables for the bar chart come from the stack mixin, which creates a legendable for each stack:

_chart.legendables = function () {
    return _stack.map(function (layer, i) {
        return {
            chart: _chart,
            name: layer.name,
            hidden: layer.hidden || false,
            color: _chart.getColor.call(layer, layer.values, i)
        };
    });
};

Given that there's currently no way to get a bar chart to display a pie chart's legend, I think the easiest thing to do is override legendables for your bar chart with its custom colors:

buttonPress.legendables = function() {
    return btnGroup.all().map(function(kv) {
        return {
            chart: buttonPress,
            // display the value as the text (not sure what you want here)
            name: kv.value,
            // apply the chart's color scale to get the color
            color: buttonPress.colors()(kv.value)
        };
    })
};

There are probably some more details to be worked out, such as what if the same value occurs twice? I am assuming you can just read the input data from the group and .map() it, but you might need to generate your data a different way.

But this should give the general idea. Lmk if it doesn't work and I'll be glad to follow up.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...