Text is not displayed on Bubbles in Bubble Chart - dc.js

I've a bubble chart built using dc.js in which the texts are not shown on the bubbles. Also, is there is a way to rotate the text on the bubbles.
Here is my code.
wfAvgChartBubble
.width(658)
.height(400)
.margins({top: 10, right: 50, bottom: 30, left: 60})
.dimension(wfStatusNameAvgDimBubble)
.group(wfStatusNameAvgGroupBubble)
.colors(d3.scaleOrdinal(d3.schemeCategory10))
.keyAccessor(function (p) {
return p.value.total;
})
.valueAccessor(function (p) {
return p.value.avg;
})
.radiusValueAccessor(function (p) {
return p.value.avg;
})
.x(d3.scaleBand().domain(data.map( function(d) {
return d['workflow_status_name'];
}
)))
.xUnits(dc.units.ordinal)
//.x(d3.scaleLinear().domain([0, 5000]))
.r(d3.scaleLinear().domain([0, 9000]))
.minRadiusWithLabel(15)
.elasticY(true)
.yAxisPadding(150)
.elasticX(true)
.xAxisPadding(300)
.maxBubbleRelativeSize(0.07)
.title(function (p) {
return p.key
+ "\n"
+ "Total Records: " + p.value.total + "\n"
+ "Average Time Taken: " + p.value.avg;
})
.render();
I need to print the text on each bubble

showing all the labels
It would be easier to test this with a running example, but I think your problem is this line:
.minRadiusWithLabel(15)
From your screenshot (thanks!) it looks like the big bubble has its label (which defaults to the key) but the labels are missing on the smaller bubbles. Try
.minRadiusWithLabel(0)
to display labels no matter what size the bubble.
(minRadiusWithLabel docs)
rotating the text
Since the text transform isn't driven by the data, it's best just to use CSS to change it, unless you need to change them individually.
Here's a reference to some of the relevant dc.js selectors.
From there we can infer a CSS rule like
g.node text {
transform: rotate(-45deg);
}

Related

display tip when mouse is anywhere over line chart

I have a composite line chart.
How can I display the respective values (date and value) when i hover on any place of the chart like below:
var composechart = dc.compositeChart("#test_composed");
composechart
.width(990)
.height(450)
.margins({ top: 50, right: 40, left: 50, bottom: 50 })
.x(d3.scaleTime().domain([new Date(2017, 0, 1), new Date(2019, 10, 30)]))
.rangeChart(xChart)
.elasticY(true)
.xUnits(d3.timeMonths)
.legend(dc.legend().x(80).y(20).itemHeight(13).gap(5))
.renderHorizontalGridLines(true)
.brushOn(false)
.compose([
dc.lineChart(composechart)
.dimension(salesgrafikDim)
.group(salesgrafikGroup,"Sales"),
dc.lineChart(composechart)
.dimension(satisgrafikDim)
.colors('red')
.group(quantitygrafikGroup,"Quantity")
])
xChart
.width(990)
.height(40)
.margins({top: 0, right: 50, bottom: 20, left: 50})
.dimension(salesgrafikDim)
.group(salesgrafikGroup)
.x(d3.scaleTime().domain([new Date(2017, 0, 1), new Date(2019, 10, 30)]))
.xUnits(d3.timeMonths);
xChart.yAxis().ticks(0);
Basically want to display a tooltip like in the screenshot below, where the mouse should not need to be over a dot for it to display.
I can do it with straight D3, with svg and append and so on. But i have dimension with other charts and dataTable, so I want to use this with dc.js.
It would be much more user friendly and easy to understand if you don't have to hover over the dots to see the tip.
My chart is below :
Since dc.js doesn't support this directly, you're going to end up dropping down into D3 in order to implement this.
First, we need to disable the existing titles:
.title(() => '')
and hover events:
composite.on('pretransition', chart => {
chart.children().forEach(
child => child.selectAll('circle.dot')
.on('mousemove', null)
.on('mouseout', null)
);
})
Then we can add a handler to put mouse handlers on the SVG itself
composite.on('postRender', chart => {
chart.svg().on('mousemove', () => { // 2
// find closest data point
const x = chart.x().invert(d3.mouse(chart.svg().node())[0] - chart.margins().left),
xs = chart.children()[0].group().all().map(kv => kv.key),
right = d3.bisectLeft(xs, x);
let closest = right;
if(right >= xs.length)
closest = right - 1;
else if(right > 0) {
// see if point to the left is closer
if(x - xs[right-1] < xs[right] - x)
closest = right - 1;
}
//console.log('closest', new Date(x), closest, xs[closest])
chart.children().forEach(child => { // 3
child.g().selectAll('circle.dot').each(function(d) {
if(d.x === xs[closest]) {
child._showDot(d3.select(this));
child.g().select('text.data-tip')
.attr('visibility', 'visible')
.attr('x', child.x()(d.x))
.attr('y', child.y()(d.y))
.text(tooltip_text(d.data))
} else
child._hideDot(d3.select(this));
});
})
})
chart.svg().on('mouseout', () => { // 4
chart.children().forEach(child => {
child.selectAll('circle.dot').each(function(d) {
child._hideDot(d3.select(this));
});
})
})
chart.children().forEach(child => child.g() // 1
.append('text')
.attr('class', 'data-tip')
.attr('fill', 'black')
.attr('alignment-baseline', 'top')
.attr('text-anchor', 'begin')
.attr('visibility', 'hidden')
)
});
I'm not going to get into the details, but this
adds a text element to each child chart, which will display the tip
listens for mousemove and finds the nearest point
shows the dots and text for the selected point in each child
hides all dots and text when the mouse leaves the chart
I ran out of time to think about this, so I didn't try to get the text positioned correctly or make it look nice.
linear scale
Linear scale fiddle.
time scale
Time scale fiddle.
As I mentioned in the comments, you're going to run into trouble with this design, if the lines get too close together. You can see this problem with the first point in this example.

set row chart labels outside the bar like a barchart in Dc.js?

How can I have the labels outside the bar in a row chart with dc.js?
I'd like a graph like this:
however, the labels are inside the actual bars... is there any settings i need to change to have it like this?
I finally got this working and here is the trick,
As mentioned by the Gordon it can be done on renderlet.
First I have gave the margin for my row chart ,
.margins({ top: 20, right: 20, bottom: 40, left: 110 })
Then took the label outside by giving x value in negative.
.labelOffsetX(-10)
This will perfectly move our label but its still need some style changes to look fine and that need to be done on renderlet,
.on('renderlet', function (chart) {
chart.selectAll("g.row text")
.style("text-anchor", "end")
.call(function (t) {
t.each(function (d) {
var self = d3.select(this);
var text = self.text();
if (text.length > 14) {
self.text('');
text = text.substring(0, 14) + '..';
self.text(text);
}
})
});
})
You can ignore .call() function as I place this just to make sure my label length does not cross certain limit.
Hope that work for you.!

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('\n')
});
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('\n')
});
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)
Is there a way to create a custom legend for this scenario?
Thanks in advance.
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.

DC.js series chart does not respond to colorAccessor logic

I have been trying to setup a common coloring scheme between a DC js pie chart and a series chart. I have created the coloring scale based on my requirement(need 20 colors for 20 topics) and then I tried to return the domain value through the colorAccessor function from the series chart. However, the colorAccessor function does not seem to work with the series chart as I tried to console.log(d) from within the colorAccesor function but nothing was logged on the console screen. And I guess that is the reason the colors are not being shared for same values across the pie chart and the series chart.
Here's my code
var TopicColorScale = d3.scale.ordinal().domain(["0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","default"])
.range(["#18a3ad", "#b85436", "#3fe825","#e82825","#8793a5","#577c77","#c1f9a2","#f2c4cd","#a4f4f9","#003366","#fff4d8","#00245e","#e5ffd8","#471b1b","#ff6666","#ff9811","#11c7ff","#8fbec9","#b5b7e0","#ffc4d9","#f6ff02"]);
//d.key sample value : "topic6,internet,advertising,online" I am extracting the topic number for the domain and want a different color for each topic
topicChart.width(350)
.height(350)
.radius(160)
.innerRadius(30)
.dimension(maxtopicVal)
.title(function(d){ return "Topic : "+d.key+"\n Maxweight sum: "+d.value+'\n Percentage: '+ Math.round((d.endAngle - d.startAngle) / Math.PI * 50) + '%';})
.group(maxtopicValGroup)
.colorAccessor(function(d){
return d.key.split(",")[0].slice(5);
})
.colors(TopicColorScale);
This works fine and I get the desired colors on the pie chart. However, when I try to plot the same with the series chart, I get the colors from the scale but same value does not map to same color across the two charts. For example topic 1 has color red on the pie chart and has color blue on the series chart. The code for the series chart is as follows and was implemented after referring to this example : http://dc-js.github.io/dc.js/examples/range-series.html
focusChart
.width(920)
.height(380)
.chart(function(c) { return dc.lineChart(c).interpolate('cardinal').evadeDomainFilter(true); })
.x(d3.scale.linear().domain([1995,2017]))
.brushOn(false)
.yAxisLabel("Topic Weight")
.xAxisLabel("Year")
.elasticY(true)
.dimension(series1Dimension)
.group(series1Group)
.colorAccessor(function(d){
return d.key.split(",")[0].slice(5);
})
.colors(TopicColorScale)
focusChart.seriesAccessor(function(d) {return " " + d.key[0];})
.keyAccessor(function(d) {return +d.key[1];})
.valueAccessor(function(d) {return +d.value;})
.legend(dc.legend().x(400).itemHeight(13).gap(1).horizontal(10).legendWidth(540).itemWidth(210));focusChart.yAxis().tickFormat(function(d) {return d3.format('d')(d);});
focusChart.xAxis().tickFormat(function(d) {return d3.format('d')(d);});
focusChart.margins().left += 20;
I am unable to figure out what the problem is(Whether there is a problem in my code or not.) It would be great if any of you could help me with the common coloring scheme between the series chart and the pie chart or nudge me in the right direction! Thank you :)
I did have a similar problem when i did try to use the colorAccessor. My solution use the same color pallet to ordinalColors and in the series chart i create the sort function to seriesSort.
// code ...
graph.ufDimension = ndx.dimension(function(d) {
return d.uf;
});
graph.stateYearDimension = ndx.dimension(function(d) {
return [d.uf, +d.year];
});
graph.ufRateGroup = graph.ufDimension.group().reduceSum(function(d) {
return +d.rate;
});
graph.stateYearRateGroup = graph.stateYearDimension.group().reduceSum(function(d) {
return +d.rate;
});
// more code ...
graph.pallet=["#FF0000","#FF6A00","#FF8C00","#FFA500","#FFD700","#FFFF00","#DA70D6","#BA55D3","#7B68EE"]
// more code ...
this.lineRateStatesByYear
.width(fw)
.height(fh)
.margins({top: 0, right: 10, bottom: 45, left: 45})
.chart(function(c) { return dc.lineChart(c).interpolate('cardinal'); })
.x(d3.scale.ordinal())
.xUnits(dc.units.ordinal)
.brushOn(false)
.yAxisLabel("km²/year")
.xAxisLabel(years[0].key + " - " + years[years.length-1].key)
.renderHorizontalGridLines(true)
.renderVerticalGridLines(true)
.title(function(d) {
return "Area/"+d.key[1]+": " + Math.abs(+(d.value.toFixed(2))) + " km²";
})
.elasticY(true)
.yAxisPadding('10%')
.dimension(this.stateYearDimension)
.group(this.stateYearRateGroup)
.mouseZoomable(false)
.seriesAccessor(function(d) {
return d.key[0];
})
.keyAccessor(function(d) {
return +d.key[1];
})
.valueAccessor(function(d) {
return +d.value;
})
.ordinalColors(graph.pallet)
.seriesSort(function(a,b) {
var rank=graph.ufRateGroup.top(Infinity);
var sr=[];
rank.forEach(function(d){
sr[d.key]=+d.value;
});
return d3.descending(sr[a], sr[b]);
})
.legend(dc.legend().x(fw - graph.lineRateStatesByYear.margins().right - 40).y(5).itemHeight(13).gap(7).horizontal(0).legendWidth(50).itemWidth(40));
this.pieTotalizedByState
.height(fh)
.width(parseInt(fw/4))
.innerRadius(10)
.externalRadiusPadding(30)
.dimension(this.ufDimension)
.group(this.ufRateGroup)
.title(function(d) {
return "Area: " + Math.abs(+(d.value.toFixed(2))) + " km²";
})
.label(function(d) {
return d.key + ":" + parseInt(Math.round(+d.value));
})
.ordinalColors(graph.pallet)
.legend(dc.legend().x(1).y(5).itemHeight(13).gap(7).horizontal(0).legendWidth(50).itemWidth(40));
I have a github repository to test and create my prototypes and the complete code is compound by files included in the entry point dashboard-prodes-rates.html. The main JS file is dashboard-prodes-rates-datatable.js where i put the charts implementation.

how to display bottom data for row chart using dc.js

I'm making a rowchart using the Dimensional Charting javascript library dc.js, which is based on d3 and crossfilter. i am display the rowchart for top(10) data.but i am trying to display rowchart for bottom(10) data but graph can be display same as the top(10) data.how to display bottom(10) data.
constRowChart.width(350)
.height(1000)
.margins({top: 20, left: 120, right: 10, bottom: 20})
.transitionDuration(750)
.dimension(density)
.group(const_total)
.renderLabel(true)
.gap(1)
.title(function (d) {
return "";
})
.elasticX(true)
.colors(d3.scale.category20c())
.ordering(function(d) { return d.value })
.xAxis().ticks(3).tickFormat(d3.format("s"));
constRowChart.data(function (group) {
return group.top(10);
});
If you don't want to create another dimension/group as #LarsKotthoff suggests, you can also use .top(Infinity) and then .splice(-10) the last ten entries off the array.
There are efficiency tradeoffs between having extra dimensions versus sorting all the data, so you might try both to see which performs better.

Resources