I'm trying to understand how jqplot calculates the width of bars when the width is not specified. Say I have the following chart:
$.jqplot(chartDiv.attr("id"), [
[
['2013-02-15', 0],
['2013-03-01', 2],
['2013-03-15', 4],
['2013-03-29', 6],
['2013-04-12', 8],
['2013-04-26', 10],
['2013-05-10', 12],
['2013-05-24', 14],
['2013-06-07', 16],
['2013-06-21', 18],
['2013-07-05', 20],
['2013-07-19', 22],
['2013-08-02', 24],
['2013-08-16', 26],
['2013-08-30', 28],
['2013-09-13', 30],
['2013-09-27', 32],
['2013-10-11', 34],
['2013-10-25', 36],
['2013-11-08', 38], , ], ], {
axes: {
xaxis: {
renderer: $.jqplot.DateAxisRenderer,
min: '2013-1-20',
max: '2013-12-1',
tickRenderer: $.jqplot.CanvasAxisTickRenderer,
tickInterval: '14 days',
tickOptions: {
angle: 45,
formatString: '%d/%m/%Y',
},
}
},
series: [{
xaxis: 'xaxis',
yaxis: 'yaxis',
renderer: $.jqplot.BarRenderer,
}],
seriesDefaults: {
shadow: false,
},
axesDefaults: {
useSeriesColor: true,
rendererOptions: {
alignTicks: true
}
},
});
When I change tickInterval between 7 days and 14 days, the width of the bars alters, despite there being the same number of bars on the same physical area. How is tickInterval used in the calculation of bar widths? Or failing that, how can I alter this example such that tickInterval can vary (it will be calculated from data eventually) but the width of the bars be set to something sensible?
In jqplot.barRenderer.js there is a property called barWidth:
// prop: barWidth
// Width of the bar in pixels (auto by devaul). null = calculated automatically.
this.barWidth = null;
When setting the series options, you can also supply rendererOptions. Adding this:
rendererOptions: { barWidth: 10 }
So the series becomes:
series: [{
xaxis: 'xaxis',
yaxis: 'yaxis',
renderer: $.jqplot.BarRenderer,
rendererOptions: { barWidth: 10 }
}],
Will force all bars to be 10 pixels wide, regardless of the tickInterval.
Edit:
The details for determining bar widths are in the setBarWidth function in jqplot.barRenderer.js.
To give you an example, calculating the bar widths for an unstacked x-axis (it's very similar for the y-axis) is as follows:
var nticks = paxis.numberTicks;
var nbins = (nticks-1)/2;
this.barWidth = ((paxis._offsets.max - paxis._offsets.min) / nbins -
this.barPadding * (nseries - 1) - this.barMargin * 2) / nseries;
Essentially, we first take the width (or height) of the axis, and divide it by the maximum number of bins (in this case bars). From this we subtract the total padding between series (in this case it is zero as there is only one series) and then subtract the margin around the outside of the bar. After that the total bar width is divided by the number of series in the plot.
As you can see from that code, the important bit is really establishing the number of ticks to display. In your particular case, this happens in the DateAxisRenderer, where it essentially finds the number of days between the max and min date ('2013-1-20' and '2013-12-1') - 315 - before dividing by the number of days in the interval, which from yoru question was either 7 or 14, giving 46 and 24 respectively.
Thanks a lot for the answer. This is old, but here is what i did to solve the problem in my case
$.jqplot.DateBarRenderer = function(){
$.jqplot.BarRenderer.call(this);
};
$.jqplot.DateBarRenderer.prototype = new $.jqplot.BarRenderer();
$.jqplot.DateBarRenderer.prototype.setBarWidth = function() {
// need to know how many data values we have on the approprate axis and figure it out.
var i;
var nvals = 0;
var nseries = 0;
var paxis = this[this._primaryAxis];
var s, series, pos;
var temp = this._plotSeriesInfo = this.renderer.calcSeriesNumbers.call(this);
nvals = temp[0];
nseries = temp[1];
var nticks = paxis.numberTicks;
var nbins = (nticks-1)/2;
// so, now we have total number of axis values.
if (paxis.name == 'xaxis' || paxis.name == 'x2axis') {
if (this._stack) {
this.barWidth = (paxis._offsets.max - paxis._offsets.min) / nvals * nseries - this.barMargin;
}
else {
this.barWidth = ((paxis._offsets.max - paxis._offsets.min)/ (nvals + 1 ) - this.barPadding * (nseries-1) - this.barMargin*2)/nseries;
//this.barWidth = ((paxis._offsets.max - paxis._offsets.min)/nbins - this.barPadding * (nseries-1) - this.barMargin*2)/nseries;
// this.barWidth = (paxis._offsets.max - paxis._offsets.min) / nvals - this.barPadding - this.barMargin/nseries;
}
}
else {
if (this._stack) {
this.barWidth = (paxis._offsets.min - paxis._offsets.max) / nvals * nseries - this.barMargin;
}
else {
this.barWidth = ((paxis._offsets.min - paxis._offsets.max)/nbins - this.barPadding * (nseries-1) - this.barMargin*2)/nseries;
// this.barWidth = (paxis._offsets.min - paxis._offsets.max) / nvals - this.barPadding - this.barMargin/nseries;
}
}
return [nvals, nseries];
};
I simply replaced the setBarWidth of the BarRenderer to use the number of values instead of the number of ticks. The rest of the code is from the BarRenderer object, keeping it unchanged in case of an update.
To use it, just use:
var plot = $.jqplot('graphID', [graphData], {
axes:{
xaxis:{
renderer:$.jqplot.DateAxisRenderer
}
},
seriesDefaults:{
renderer:$.jqplot.DateBarRenderer
}
});
Related
I am trying to create an area chart that looks like this where I am using bars:
var trace1 = {
x: ['2013-10-04 9:00:00', '2013-10-04 9:30:00', '2013-10-04 10:00:00', '2013-10-04 11:00:00', '2013-10-04 11:30:00', '2013-10-04 12:30:00'],
y: [20, 20, 10, 10, 20, 20],
type: 'bar',
base: [5,5,5,5,5,5],
mode: 'none'
};
var data = [trace1];
myDiv = document.getElementById('myDiv');
Plotly.newPlot(myDiv, data);
Pen
As you can see y axis starts from non zero value. Is it even possible?
Thanks
You can add this code to start y-axis 0
var layout = {
yaxis: {
rangemode: 'tozero' //or below
//range: [0, 25]
}
};
Plotly.newPlot(myDiv, data, layout);
I am using c3.js to show temperature values, as a range I expect values from -30 to +50 degrees.
This works fine so far, but I am unhappy with the graphical representation.
I would like to have my X axis meet the Y axis at 0 and not at -30. Is this possible with c3.js? I already had a look at the manual and the examples, but I didn't find anything regarding offsetting the axis in this way.
You can hide default axis (1) and add custom line at zero (2).
See in action (jsfiddle)
var chart = c3.generate({
data: {
columns: [
['sample', 30, 20, -10, 40, 15, -25]
]
},
axis: {
x: {
show: false // (1)
},
y: {
max: 50,
min: -30,
// center: 0,
}
},
grid: {
y: {
lines: [{ value: 0, text: 'zero' }] // (2)
},
},
});
Related docs:
http://c3js.org/reference.html#axis-x-show
http://c3js.org/reference.html#grid-y-lines
I am following this tutorial https://bytutorial.com/blogs/open-source-chart/learn-how-to-create-multiple-bar-charts-with-multiple-line-charts-in-nvd3 which is great .
Even though, it has a colorfull way to display the key values. Instead of the name of the key, shows a div with the background color of the key:
<div class='tooltip'>
<span>Legend:</span>
<div style='background:" + key.series[0].color + ";display:inline-block;height:150px;width:150px;'> </div>
</div>
How can I show the name of the key?, instead of its color.
This is my graph: http://codepen.io/neonpulp/pen/zBZgrv . Below is the full code also.
function buildGraph(){
//this will hold of our main data consists of multiple chart data
var data = [];
//variables to hold monthly month
var monthList = ['Ene 2016','Feb 2016','Mar 2016','Apr 2016','May 2014','Jun 2016','Jul 2016','Aug 2016', 'Sep 2016', 'Oct 2016', 'Nov 2016', 'Dec 2016'];
var monthlyIncome = [0, 2757820, 3447270, 0, 0, 0, 0, 0, 0, 0, 0, 0];
var companiesNumber = [0, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0];
//Array to hold each individual coordinate x and y values
var monthlyIncomeValues = [];
var companiesNumberValues = [];
//Looping the data and fetch into array
for(var i = 0; i < monthList.length; i++){
var xyIncome = {x: i, y: monthlyIncome[i]};
monthlyIncomeValues.push(xyIncome);
var xyCompanies = {x: i, y: companiesNumber[i]};
companiesNumberValues.push(xyCompanies);
}
//bar chart
var dataIncome = { key: "Monthly Income", values: monthlyIncomeValues, type: "bar", yAxis: 2, color: '#00913B' }
//line chart
var dataCompanies = { key: "Amount of companies", values: companiesNumberValues, type: "line", yAxis: 1, color: '#00C3D9' }
//Insert the values array into data variable
data.push(dataIncome);
data.push(dataCompanies);
//build the graph
nv.addGraph(function () {
//build as multichart graphs and set the margin right and left to 100px.
var chart = nv.models.multiChart()
.margin({left: 100, right: 100})
//customize the tool tip
chart.tooltip.contentGenerator(function (key, x, y, e, graph) {
return "<div class='tooltip'><span>Month:</span> " + monthList[key.index] + "</div>" + "<div class='tooltip'><span>Value:</span> " + key.series[0].value + "</div><div class='tooltip'><span>Legend:</span> <div style='background:" + key.series[0].color + ";display:inline-block;height:15px;width:15px;'> </div></div>";
});
//Overwrite the x axis label and replace it with the month name
chart.xAxis.tickFormat(function (d) { return monthList[d] });
//Chart Interpolate
chart.interpolate("linear")
//Dollar Sign
chart.yAxis2.tickFormat(function(d) { return '$' + d3.format(',f')(d) });
//get the chart svg object and fecth the data to build the chart
d3.select('#chart svg')
.datum(data)
.transition().duration(500).call(chart);
return chart;
});
}
//call the function to build the graph.
buildGraph();
I'm trying to create a "linePlusBar" chart with NVD3 . The x-axis values are the months of the year:
var monthList = ['Ene 2016','Feb 2016','Mar 2016','Apr 2016','May 2014','Jun 2016','Jul 2016','Aug 2016', 'Sep 2016', 'Oct 2016', 'Nov 2016', 'Dec 2016'];
For some reason only the bar graph shows this value in the tooltip, for instance Month:Aug 2016.
The line graph instead shows this value in the tooltip as "Month: undefined".
What could be the problem?
This is my graph: http://codepen.io/neonpulp/pen/zBZgrv . Below is the code also.
function buildGraph(){
//this will hold of our main data consists of multiple chart data
var data = [];
//variables to hold monthly month
var monthList = ['Ene 2016','Feb 2016','Mar 2016','Apr 2016','May 2014','Jun 2016','Jul 2016','Aug 2016', 'Sep 2016', 'Oct 2016', 'Nov 2016', 'Dec 2016'];
var monthlyIncome = [0, 2757820, 3447270, 0, 0, 0, 0, 0, 0, 0, 0, 0];
var companiesNumber = [0, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0];
//Array to hold each individual coordinate x and y values
var monthlyIncomeValues = [];
var companiesNumberValues = [];
//Looping the data and fetch into array
for(var i = 0; i < monthList.length; i++){
var xyIncome = {x: i, y: monthlyIncome[i]};
monthlyIncomeValues.push(xyIncome);
var xyCompanies = {x: i, y: companiesNumber[i]};
companiesNumberValues.push(xyCompanies);
}
//bar chart
var dataIncome = { key: "Monthly Income", values: monthlyIncomeValues, type: "bar", yAxis: 2, color: '#00913B' }
//line chart
var dataCompanies = { key: "Amount of companies", values: companiesNumberValues, type: "line", yAxis: 1, color: '#00C3D9' }
//Insert the values array into data variable
data.push(dataIncome);
data.push(dataCompanies);
//build the graph
nv.addGraph(function () {
//build as multichart graphs and set the margin right and left to 100px.
var chart = nv.models.multiChart()
.margin({left: 100, right: 100})
//customize the tool tip
chart.tooltip.contentGenerator(function (key, x, y, e, graph) {
return "<div class='tooltip'><span>Month:</span> " + monthList[key.index] + "</div>" + "<div class='tooltip'><span>Value:</span> " + key.series[0].value + "</div><div class='tooltip'><span>Legend:</span> <div style='background:" + key.series[0].color + ";display:inline-block;height:15px;width:15px;'> </div></div>";
});
//Overwrite the x axis label and replace it with the month name
chart.xAxis.tickFormat(function (d) { return monthList[d] });
//Chart Interpolate
chart.interpolate("linear")
//Dollar Sign
chart.yAxis2.tickFormat(function(d) { return '$' + d3.format(',f')(d) });
//get the chart svg object and fecth the data to build the chart
d3.select('#chart svg')
.datum(data)
.transition().duration(500).call(chart);
return chart;
});
}
//call the function to build the graph.
buildGraph();
The reason is that key for line chart doesn't include index - it has pointIndex instead.
Try this:
monthList[key.index || key.pointIndex]
Demo: http://codepen.io/LukaszWiktor/pen/xOdKmQ
I want to put a label to the CanvasOverlay Horizontal line and show it in the graph. Haven't found any documentation related to it. But was not successful. Any pointer to fix this issue would be appreciated.
var line3 = [['02/01/2012 00:00:00', '02/01/2012 01:00:00'], ['02/02/2012 00:00:00', '02/01/2012 06:00:00'], ['02/03/2012 00:00:00', '02/01/2012 06:00:00'], ['02/04/2012 00:00:00', '02/01/2012 06:00:00']];
var plot2 = $.jqplot('chart1', [line3], {
title:'Mouse Cursor Tracking',
axes:{
xaxis:{
min:'2012-02-01',
max:'2012-02-10',
Label: 'Day',
renderer:$.jqplot.DateAxisRenderer,
tickOptions:{
formatString:'%b %#d'
},
tickInterval:'1 day'
},
yaxis:{
min:'2012-02-01 00:00:00',
max:'2012-02-01 24:00:00',
Label: 'Time',
renderer:$.jqplot.DateAxisRenderer,
tickOptions:{
formatString:'%H'
},
tickInterval:'2 hour'
}
},
highlighter: {
show: false
},
cursor: {
show: true,
tooltipLocation:'sw'
},
canvasOverlay: {
show: true,
objects: [
{horizontalLine: {
name: 'pebbles',
y: new $.jsDate( '2012-02-01 05:00:00').getTime(),
lineWidth: 3,
color: 'rgb(100, 55, 124)',
shadow: true,
lineCap: 'butt',
xOffset: 0
}},
{dashedHorizontalLine: {
name: 'bam-bam',
y: new $.jsDate( '2012-02-01 10:00:00').getTime(),
lineWidth: 4,
dashPattern: [8, 16],
lineCap: 'round',
xOffset: '25',
color: 'rgb(66, 98, 144)',
shadow: false
}}
]
}
});
I recently had this same problem and came up with a solution that seems to work pretty well. First of all, you'll need to create a new function so that you can pass in the plot object "plot2". You can then access the various properties of your axes to help calculate where jqplot is rendering your horizontal line.
function applyChartText(plot, text, lineValue) {
var maxVal = plot.axes.yaxis.max;
var minVal = plot.axes.yaxis.min;
var range = maxVal + Math.abs(minVal); // account for negative values
var titleHeight = plot.title.getHeight();
if (plot.title.text.indexOf("<br") > -1) { // account for line breaks in the title
titleHeight = titleHeight * 0.5; // half it
}
// you now need to calculate how many pixels make up each point in your y-axis
var pixelsPerPoint = (plot._height - titleHeight - plot.axes.xaxis.getHeight()) / range;
var valueHeight = ((maxVal - lineValue) * pixelsPerPoint) + 10;
// insert the label div as a child of the jqPlot parent
var title_selector = $(plot.target.selector).children('.jqplot-overlayCanvas-canvas');
$('<div class="jqplot-point-label " style="position:absolute; text-align:right;width:95%;top:' + valueHeight + 'px;">' + text + '</div>').insertAfter(title_selector);
}
You're essentially grabbing the size of your graph's div, then subtracting out the # of pixels that make up the graph's title and the text of the x-axis labels. Then you can calculate how many pixels make up each point in your y-axis. Then it's just a matter of seeing where your line fits within the range and applying your label accordingly. You may have to tweak it in a few places, but this should work pretty well.