Using nvd3 multibar chart. When having minimum bars x axis label does not display in center like y axis.
Sample code to generate x axis label.
chart = nv.models.multiBarChart().margin({
top: 30,
right: 28,
bottom: 50,
left: 60
})
.x(function(d) {
return d.x
})
.y(function(d) {
return d.y
})
.color(d3.scale.myColors().range());
chart.xAxis
.showMaxMin(true)
.axisLabel(xAxisLabel)
.tickFormat(xAxisTickFormat)
.tickValues(xaxisValues);
How to solve this problem ?
This might sound strange but try swapping the order of axisLabel and showMaxMin like the following :
chart.xAxis
.axisLabel(xAxisLabel) // axisLabel first
.showMaxMin(true)
.tickFormat(xAxisTickFormat)
.tickValues(xaxisValues);
Hope it helps.
I have used the next workaround. Hope it helps:
var transitions = 0;
d3.select(this.tag + ' svg').transition().each( "start", function() {
transitions++;
}).each( "end", function() {
if( --transitions === 0 ) {
alignaxisCallback();
}
});
function alignaxisCallback(){
var axisWidth = d3.select('.nv-x').node().getBoundingClientRect().width;
d3.select('.nv-x').select('.nv-axislabel').attr('x', axisWidth / 2);
}
Did you try
chart.xAxis.axisLabelDistance(LABEL_OFFSET);
The issue is resolved by updating nvd3 to version 1.8.4 or higher.
Related
Please forgive my english.
I am trying to create a line chart with ordinal scale and brushon function. I successfully set brushing on ordinal thanks to this method :
https://dc-js.github.io/dc.js/examples/brush-ordinal.html
And now I need to set the x-axis values order.
My data is like this :
[{"id_commune":4,"datation":"-30/-20","effectif":0.09,"commune":"Frejus","lieu_dit":"Les Aiguieres","departement":"83","pays":"FR","longitude":6.73703399,"latitude":43.433152,"quantite":1,"id_datation":1},
{"id_commune":4,"datation":"-20/-10","effectif":0.09,"commune":"Frejus","lieu_dit":"Les Aiguieres","departement":"83","pays":"FR","longitude":6.73703399,"latitude":43.433152,"quantite":1,"id_datation":2},
{"id_commune":4,"datation":"-10/1","effectif":0.09,"commune":"Frejus","lieu_dit":"Les Aiguieres","departement":"83","pays":"FR","longitude":6.73703399,"latitude":43.433152,"quantite":1,"id_datation":3},
{"id_commune":7,"datation":"20/30","effectif":0.33,"commune":"Nimes","lieu_dit":"Solignac","departement":"30","pays":"FR","longitude":4.36005399,"latitude":43.836699,"quantite":1,"id_datation":6},{"id_commune":6,"datation":"20\/30","effectif":0.6,"commune":"Muralto","lieu_dit":"Liverpool b","departement":"TI","pays":"CH","longitude":8.80560809,"latitude":46.1729618,"quantite":1,"id_datation":6},
{"id_commune":4,"datation":"20/30","effectif":0.09,"commune":"Frejus","lieu_dit":"Les Aiguieres","departement":"83","pays":"FR","longitude":6.73703399,"latitude":43.433152,"quantite":1,"id_datation":6},{"id_commune":1,"datation":"20/30","effectif":0.14,"commune":"Aislingen","lieu_dit":"NP","departement":"Lkr. Dillingen an der Donau BY","pays":"DE","longitude":10.4559987,"latitude":48.5065603,"quantite":1,"id_datation":6},]
My crossfilter dimension and group look like this :
var ndx = crossfilter(records)
var graphDim = ndx.dimension(function(d) {return d.datation});
var graphGroup = graphDim.group().reduceSum(function(d) {return d.effectif;});
And the chart :
lineChart
.width(950)
.height(350)
.margins({top: 10, right: 50, bottom: 35, left: 30})
.dimension(graphDim)
.keyAccessor(function(kv) { return graphGroup.ord2int(kv.key); })
.group(graphGroup)
.x(d3.scaleLinear().domain(linear_domain))
.xAxisLabel("Chronologie")
.yAxisLabel("Effectif")
.brushOn(true)
.renderArea(true)
.renderHorizontalGridLines(true)
.renderVerticalGridLines(true)
.elasticY(true)
.yAxis().ticks(4);
Right now, I get this :
The result I am trying to accomplish is the same chart but with x-axis ticks values ordered like this :
"-30/-20
-20/-10
-10/1
..."
Any help appreciated. Thank you!
Welcome to Stack Overflow. For future reference, it helps if you can include a reproducible example, for example a jsFiddle.
I understood what you were doing because I wrote the ordinal brushing example, but you didn't include all the code, so it wouldn't be clear to others.
Here is a fiddle with complete code, and here is the relevant code:
var graphDim = ndx.dimension(function(d) {return d.datation});
var graphGroup = graphDim.group().reduceSum(function(d) {return d.effectif;});
graphGroup = ordinal_to_linear_group(sort_group(graphGroup, function(a, b) {
return d3.descending(a.value, b.value);
}));
line = dc.lineChart('#line');
var linear_domain = [-0.5, data.length - 0.5];
line
.width(950)
.height(350)
.margins({top: 10, right: 50, bottom: 35, left: 30})
.dimension(graphDim)
.keyAccessor(function(kv) { return graphGroup.ord2int(kv.key); })
.group(graphGroup)
.x(d3.scaleLinear().domain(linear_domain))
.xAxisLabel("Chronologie")
.yAxisLabel("Effectif")
.brushOn(true)
.renderArea(true)
.renderHorizontalGridLines(true)
.renderVerticalGridLines(true)
.elasticY(true)
line.yAxis().ticks(4);
line.xAxis()
.tickValues(d3.range(data.length))
.tickFormat(function(d) { return graphGroup.int2ord(d); });
Here is the (bad) result:
As its name implies, sort_group() creates a fake group sorted by the ordering function. Right now it's sorting by value, from highest to lowest:
sort_group(graphGroup, function(a, b) {
return d3.descending(a.value, b.value);
})
It looks like you want to sort by the numeric value of the first part of each key. You can change the sort accordingly:
sort_group(graphGroup, function(a, b) {
return d3.ascending(+a.key.split('/')[0], +b.key.split('/')[0]);
})
This splits each key by /, then takes the first item ([0]), then converts it to a number (+), before using d3.ascending to specify sorting from low to high.
The result:
And a working version of the fiddle.
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.!
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.
I'm trying to draw the chart with data in following format:
[
{'label':'0', 'seconds':1094},
{'label':'1', 'seconds':1096},
{'label':'2', 'seconds':1112},
...
]
and the result looks like this:
It looks good and the question is:
How to customize the format of displaying Y value to this?:
UPDATE:
The code building the chart
nv.addGraph(function() {
var chart = nv.models.multiBarHorizontalChart()
.x(function(d) { return d.label })
.y(function(d) { return d.value })
.margin({top: 30, right: 20, bottom: 50, left: 20})
.showValues(true) //Show bar value next to each bar.
.tooltips(false) //Show tooltips on hover.
.transitionDuration(350)
.showControls(true); //Allow user to switch between "Grouped" and "Stacked" mode.
chart.yAxis
.tickFormat(d3.format(',.0f'));
d3.select('#chart1 svg')
.datum(jsonData)
.call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
Try something like this
chart.yAxis.tickFormat(function(d) {
// %M - minute as a decimal number [00,59].
// %L - milliseconds as a decimal number [000, 999].
return d3.time.format('%M%L')(new Date(d))
});
UPDATE :
// Line Not Tested but valueFormat should do the trick
chart.valueFormat(d3.time.format('%M%L'));
Take a look at this if you need more time formatting options.
Hope it helps.
I'm trying to set the width and height of a nvd3 multi bar chart programmatically using
chart.width(600);
chart.height(400);
See the example here:
http://jsfiddle.net/hPgyq/20/
As you can see this really messes up the chart. I know I can do this is CSS with:
#chart svg {
width: 600px;
height: 400px;
}
but I thought this was also possible using the width() and height() functions on the chart. Am I doing something wrong here or am I mis-using the two functions?
Yes it is possible, like you have specified the width & height of the chart, you have to use the d3.select and set its width & height attribute.
Changes to the code are below and there is a version of the code here
function visualizeData(data) {
nv.addGraph(function() {
var width = 600, height = 400;
chart = nv.models.multiBarChart().x(function(d) {
return d.x;
}).y(function(d) {
return d.y;
}).color(['#aec7e8', '#7b94b5', '#486192']).stacked(true)
//.margin({top:150,right:150,bottom:150,left:150})
.width(width).height(height);
chart.multibar.hideable(false);
chart.xAxis.showMaxMin(true).tickFormat(d3.format(',f'));
chart.yAxis.tickFormat(d3.format(',.1f'));
d3.select('#chart svg').datum(data).transition().duration(500).call(chart).style({ 'width': width, 'height': height });
nv.utils.windowResize(chart.update);
return chart;
});
}
For the AngularJs version (angular-nvd3), I had to add the height either in chart options and as an attribute:
chart.html
<nvd3 options='vm.chartOptions' data='vm.chartData' height="250" config="vm.chartConfig">
</nvd3>
chart.controller.js
vm.chartOptions = {
chart : {
height : 250,
type : "pieChart",
donut : true,
showLegend : false,
//The rest of the configuration
};
As it is told in the comments, first seems to control the height of the inner svg and the second does the global chart height.