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.
Related
I am working on a Gantt-like task display using HTML5 canvas, and the Konvajs canvas library.
Deconstructing a Gantt chart into its components leads me currently to a view as below. Here 1 is the list of tasks, 2 is the task bar area, 3 is a task bar, and 4 is a text cell.
For this question I am seeking code to construct 1 & 4. The data to be displayed will be delivered in plain JS objects with a nested list of tasks where each task has a number, name, assigned-to person name, start date, end date, days duration, and % complete.
So the requirement is to be able to construct a spreadsheet-like panel such as is seen on the left hand side of a Gantt chart.
I have something part developed which I shall post as an answer. However this seems like such as common need that I am hoping there is someone out there with code they can cut & paste into SO to lead the way.
Note: Gantt in sample image is from Vertex42.
So here is my own fumbling attempt at an approach. Can anyone improve upon it or am I going down the wrong road.
EDIT: I now have a fledgling component for drawing text into the spreadsheet-like cells, including the percent complete shading. To keep this answer uncluttered, this additional component is in this codepen.
// this is the object that defines our grid
//
var gridData = { name: 'grid1', width: 350, height: 400, rowHeight: 24, padding: 4, fill: 'azure', gridLineColor: '#ccc', header: {size: 16, fill: 'black', color: 'white' }, data: {size: 16, fill: 'azure', color: 'black' },
row: [
{ cells: // row 1
[
{ width: 50, text: 'Item', style: 'header'},
{ width: 240, text: 'Name', style: 'header'},
{ width: 60, text: 'Status', style: 'header'},
]
},
{ cells: // row 2
[
{ text: '1'},
{ text: 'Find tea pot'},
{ text: '100%'},
]
},
{ cells: // row 3
[
{ text: '2'},
{ text: 'Boil water'},
{ text: '60%'},
]
}
]
}
// From here on could be wrapped into a component that churns out grids. Maybe you pass in the id of the stage container
// and the data model you want to produce, etc.
// Set up the canvas / stage
var stage = new Konva.Stage({container: 'container1', width: 600, height: 300});
// Add a layer
var layer = new Konva.Layer({draggable: false});
stage.add(layer);
// make a main group for the grid, call it a panel. Assigning a name may be handy later
var panel = new Konva.Group({name: gridData.name});
layer.add(panel); // Add the panel to the layer
// a group has no visual properties. Add a background rect to hold the colour fill
var panelRect = new Konva.Rect({width: gridData.width, height: gridData.height, fill: gridData.fill})
panel.add(panelRect);
var topLeft = {x: 0, y: 0}; // Since we are drawing a grid, we need to compute the position of each cell
for (var i = 0; i < gridData.row.length; i = i + 1){
topLeft.x = 0; // reset x at start of each row
// iterate for each cell on the row
for (var j = 0; j < gridData.row[i].cells.length; j = j + 1){
var cell = new Konva.Rect({name: 'cellBg', // assign a name for later searching
x: topLeft.x, y: topLeft.y, // position as computed
width: gridData.row[0].cells[j].width, // use the first row from celldate to get the cell width
height: gridData.rowHeight, // grid has a uniform row height
stroke: gridData.gridLineColor, // and line colour
strokeWidth: 1, // use a set line width but you can add to the gridData object as needed.
fill: (i === 0 ? gridData.header.fill : gridData.data.fill), // use the given header text color
});
panel.add(cell);
// Add text to the cell. Note that if you wanted to be using alignments you would need to draw the text off-screen and
// get width/height of that text then use those values for positioning calculations. Once you have the rect size of the
// text, all the alignments are simple math.
var text = new Konva.Text({ x: topLeft.x + gridData.padding, // add padding to locate the text nicely into the cell
y: topLeft.y + gridData.padding,
// use the given text size
fontSize: (i === 0 ? gridData.header.size : gridData.data.size),
// use the given header text color
fill: (i === 0 ? gridData.header.color : gridData.data.color),
text: gridData.row[i].cells[j].text, // set the text value.
listening: false // stop text interfering with mouse events
});
panel.add(text);
cell.on('mouseover', function(evt){
var shape = evt.target;
$(shape).data('bgColor', shape.fill());
shape.fill('lime');
layer.draw();
})
cell.on('mouseout', function(evt){
var shape = evt.target;
shape.fill($(shape).data('bgColor'));
layer.draw();
})
topLeft.x = topLeft.x + gridData.row[0].cells[j].width; // offset the computed next cell x value by the width of the cell
}
topLeft.y = topLeft.y + gridData.rowHeight; // offset the computed next cell y value by the height of the row
}
layer.draw();
stage.draw();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/2.5.1/konva.min.js"></script>
<div id='container1' style="width: 300px, height: 200px; background-color: silver;"></div>
I am making a stacked multi chart bar graph like this one
http://nvd3.org/examples/multiBar.html
Till now I am able to push my values on Y- axis and X axis too but the problem I am facing is that the all the values are not getting displayed on the x axis but only 10 values are getting displayed . I am using nvD3 library in my angular code . and displaying date on x axis.
$scope.options1 = {
chart: {
type: 'multiBarChart',
height: 600,
margin: {
top: 20,
right: 20,
bottom: 200,
left: 45
},
clipEdge: false,
duration: 500,
stacked: true,
groupSpacing: 0.1,
useInteractiveGuideline: true,
showMaxMin: false,
xAxis: {
axisLabel: 'Timeline',
showMaxMin: false,
tickFormat: function(d) {
return d3.time.format('%d-%m-%y')(new Date(d))
},
xScale:d3.time.scale(),
rotateLabels: '-70'
},
yAxis: {
axisLabel: 'Pending Bills',
axisLabelDistance: -20,
groupSpacing: 0.1,
tickFormat: function(d) {
return d3.format(',f')(d);
}
}
}
};
generating ticking value array using this function
$scope.options1.chart.xAxis.tickValues = function() {
var xTick = _.map(data.data.data[0].values, function(value) {
return value.x;
});
xTick = _.sortBy(xTick, function(date){ return new Date(date); });
console.log(xTick);
return xTick;
}
the output of the console.log(xTick) is something like this which is all dates -
["2015-09-01", "2015-09-02", "2015-09-03", "2015-09-04", "2015-09-05",
"2015-09-06", "2015-09-07", "2015-09-08", "2015-09-09", "2015-09-10",
"2015-09-11", "2015-09-12", "2015-09-13", "2015-09-14", "2015-09-15",
"2015-09-16", "2015-09-17", "2015-09-18", "2015-09-19", "2015-09-20",
"2015-09-21", "2015-09-22", "2015-09-23", "2015-09-24", "2015-09-25",
"2015-09-26", "2015-09-27", "2015-09-28", "2015-09-29", "2015-09-30",
"2015-10-01", "2015-10-02", "2015-10-03", "2015-10-04", "2015-10-05",
"2015-10-06", "2015-10-07", "2015-10-08", "2015-10-09", "2015-10-10",
"2015-10-11", "2015-10-12", "2015-10-13", "2015-10-14", "2015-10-15",
"2015-10-16", "2015-10-17", "2015-10-18", "2015-10-19", "2015-10-20"]
as much I read about the it. all the dates should be get plotted on x axis but they are not
If you want to display all the ticks on X-Axis, you can add this option to your chart options :
"reduceXTicks": false,
For extended option page you can visit :
Angular NVD3 - MultiBarChart
Hope it helps.
chart: {
type: 'chartType',
xAxis: {
ticks:8
}
}
You can try "ticks" property if you want to display a specific number of ticks on the axis.
[[["09251A0428",90],["10251A0547",37]],[["09251A0428",4],["10251A0547",54]]]
Above data contains two series. x values of each series are same. if the x values are numeric the jqplot displays line chart with two series as normal. but we need to display strings on x axis and for each string corresponding series values.
How to set strings on xaxis for multiple series line chart of jqplot?
I have preapared an example for you based on the data you gave:
JsFiddle link
$.jqplot.config.enablePlugins = true;
var chartData = [[["09251A0428",90],["10251A0547",37]],[["09251A0428",4],["10251A0547",54]]];
function PlotChart(chartData) {
var plot2 = $.jqplot('chart1', chartData, {
title: 'Mouse Cursor Tracking',
seriesDefaults: {
pointLabels: {
show: true
}
},
axes: {
xaxis: {
pad: 1,
// a factor multiplied by the data range on the axis to give the
renderer: $.jqplot.CategoryAxisRenderer,
// renderer to use to draw the axis,
tickOptions: {
formatString: '%b %#d'
}
},
yaxis: {
}
},
highlighter: {
sizeAdjust: 7.5
},
cursor: {
show: true
}
});
}
PlotChart(chartData);
hFollowing is the code for barclustered jqplot. can anyone please guide me gow to create a highlighted array in following code dynamically
$(document).ready(function(){
// For horizontal bar charts, x an y values must will be "flipped"
// from their vertical bar counterpart.
var plot2 = $.jqplot('chart2', [
[[2,1], [4,2], [6,3], [3,4]],
[[5,1], [1,2], [3,3], [4,4]],
[[4,1], [7,2], [1,3], [2,4]]], {
seriesDefaults: {
renderer:$.jqplot.BarRenderer,
// Show point labels to the right ('e'ast) of each bar.
// edgeTolerance of -15 allows labels flow outside the grid
// up to 15 pixels. If they flow out more than that, they
// will be hidden.
pointLabels: { show: true, location: 'e', edgeTolerance: -15 },
// Rotate the bar shadow as if bar is lit from top right.
shadowAngle: 135,
// Here's where we tell the chart it is oriented horizontally.
rendererOptions: {
barDirection: 'horizontal'
}
},
axes: {
yaxis: {
renderer: $.jqplot.CategoryAxisRenderer
}
}
});
});
You have a problem with your Javascript syntax and algorithm. The loop should looks like :
VData="9,453,470,232|488,378,375,142|365,275,255,434|217,317,479,89";
var a = new Array();
var split_fst = VData.split("|")
for(m=0;m<split_fst.length;m++) {
var split_snd = split_fst[m].split(",");
a[m] = new Array();
for(j=0;j<split_snd.length;j++){
a[m][j]=split_snd[j];
}
}
Your a variable now looks like : `[["9","453","470","232"],["488","378","375","142"],["365","275","255","434"],["217","317","479","89"]]
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
}
});