I'm trying to limit the number of ticks on a dynamic jqplot I'm setting up. Unfortunately, when I do it, the points do not look accurate. Here's what it looks like:
As you can see, the point on 10/05/12, for example, is not on it's correct axis. I realize that now these points are estimated, but the points must look accurate on the graph -- especially when hovering over the points gives you their correct date and y-axis value.
Here's my code for this intilization, if that helps:
var datepickerBegin = $("#datepicker_start").datepicker({dateFormat: "dd/mm/yyyy"}).val();
var datepickerEnd = $("#datepicker_to").datepicker({dateFormat: "dd/mm/yyyy"}).val();
var startDiff = $("#datepicker_start").datepicker("getDate");
var endDiff = $("#datepicker_to").datepicker("getDate");
alert(startDiff)
alert(endDiff)
// Check if dates are more than a week apart
if(endDiff - startDiff >= 15 * 86400 * 1000) {
var plot2 = $.jqplot('chart2', jsonurl,{
title: "Storage Space",
dataRenderer: ajaxDataRenderer,
dataRendererOptions: {unusedOptionalUrl: jsonurl},
axes: {
xaxis: {
'numberTicks' : 15,
min: datepickerBegin,
max: datepickerEnd,
renderer:$.jqplot.DateAxisRenderer,
rendererOptions:{tickRenderer:$.jqplot.CanvasAxisTickRenderer},
tickInterval: '1 day',
tickOptions:{formatString:'%#m/%#d/%Y'
}
},
yaxis: {
label: "MB",
tickOptions:{formatString:'%d '},
min: 0
}
},
highlighter: {
show: true,
sizeAdjust: 7.5
}
});
// Somehow fix the line?
plot2.redraw({});
}
Anyone out there have a fix for this? Thanks!
Related
I use Highcharts to plot the graph, but there is a problem I am facing. data contains the values from 1 to n. So I want the yAxis column to start at 1. I set yAxis min = 1 but it doesn't fix my problem.
Here is a jsfiddle that illustrates the problem I am having:
Link
yAxis: {
title: null,
endOnTick: false,
min: 1
}
Is there any way to solve my problem? I expect it like this:
I expect it like this
The simplest solution is to use tickPositioner function and modify the first tick. For example:
yAxis: {
title: null,
tickPositioner: function() {
const tickPositions = [...this.tickPositions];
tickPositions[0] = 1;
return tickPositions;
},
endOnTick: false,
startOnTick: false,
min: 1
}
Live demo: https://jsfiddle.net/BlackLabel/rf3cm8Lw/
API Reference: https://api.highcharts.com/highcharts/xAxis.tickPositioner
Thanks for ppotaczek answer. I find it pretty cool, can ignore blanks, starting from the smallest element. I have a solution for this and am posting it here for anyone who needs it.
tickPositioner: function () {
var positions = [],
tick = Math.floor(this.dataMin),
increment = Math.ceil((this.dataMax - this.dataMin) / 6);
if (this.dataMax !== null && this.dataMin !== null) {
for (tick; tick - increment <= this.dataMax; tick += increment) {
positions.push(tick);
}
}
return positions;
}
So i'm currently trying to fix a problem with a Google Charts. I am drawing a chart that shows numeric values on the Y-axis and dates on the X-axis. I want the date to adpat according to the specified timezone. For that i'm using the DateFormatter Object from Google charts, providing the patter and the Timezone like so:
var dateFormatter = new google.visualization.DateFormat({
timeZone: _timeZone, //timezone
pattern: $scope.selectedResolution.pattern
});
The pattern attribute receives a string containing a format string and the timeZone receves a value representing the number of hours regarding the timezone offset (example, -4, -5, etc).
I Format the chart values like so:
dateFormatter.format($scope.data, 0);
Being that the column "0" is the column where i have my dates.
I then draw the chart, standard stuff:
chart = new google.visualization.ComboChart(document.getElementById(chart_name));
//generateTicks($scope.data, options);
chart.draw($scope.data, options);
The thing is that, the values showed in the X-Axis are not formatted whatsover, be it in the format or the timezon.
Problem Showcase
As you can see in the image above, the X-Axis shows "13:00" where it should show "15:00", in this particular case.
I also went and check the data i'm feeding the chart with to see if the formatter was actually doing any work on the data itself and it appears to be working properly:
Chart Data
Also, here's my charts options for reference:
var series = {}, firstSerie, row, title, tempRows = [];
// Create the data table
series[0] = {targetAxisIndex: 0, color: '#3182BD'};
series[1] = {color: '#FF7F00', type: 'line'};
options = {
tooltip: {isHtml: true},
titleTextStyle: {fontSize: 12, bold: false},
isStacked: false,
backgroundColor: 'transparent',
legend: {position: 'none'},
height: 300,
pointSize: 1,
series: series,
vAxes: {
// Adds titles to each axis.
0: {
textStyle: {color: $scope.widgetOptions.text_color || '#333333'},
format: $scope.vLabel === '%' ? '#\'%\'' : generateVerticalAxisPattern() + $filter('trustedHtml')($scope.vLabel),
minValue: 0,
gridlines: {
color: 'transparent'
}
}
},
hAxis: {
//title: graphLegend,
textStyle: {color: $scope.widgetOptions.text_color || '#333333'},
titleTextStyle: {color: $scope.widgetOptions.text_color || '#333333'},
viewWindows: {
min: moment().subtract(24, 'hours'),
max: moment()
},
gridlines: {
color: 'transparent',
units: {
hours: {format: ['HH:mm', 'ha']}
},
count: -1
},
minorGridlines: {
count: 0
}
},
vAxis: {
viewWindowMode: 'maximized',
viewWindow: {
min: $scope.widgetOptions.lower_limit
}
},
animation: {
duration: 500,
easing: 'out'
}
};
So, does anyone have any clue of what's going on? Im working with angularjs btw.
Thanks in advance
the format method, as follows, only formats the data, not the chart or chart axis.
dateFormatter.format($scope.data, 0);
basically, the above is used to display the correct format in the tooltip when hovering a data point.
to format an axis, you would normally set the format configuration option.
hAxis: {
format: $scope.selectedResolution.pattern
}
however, in this case, since you are changing the time zone,
you will need to provide custom ticks on the x-axis.
you can build an array of ticks from the data table rows,
using the formatValue method from the formatter.
var ticks = [];
for (var i = 0; i < $scope.data.getNumberOfRows(); i++) {
var dateValue = $scope.data.getValue(i, 0);
ticks.push({
v: dateValue,
f: dateFormatter.formatValue(dateValue)
});
}
// then add above to ticks to options...
hAxis: {
ticks: ticks
}
each tick will need to be an object,
with properties for v: for value,
and f: for formatted value.
the only problem with the above routine,
it assumes you want to display a tick for each row in the data table.
if you do not have room to display them all,
you will need to figure out your own algorithm to display the correct number of ticks.
edit
another method is to find the min and max dates within the data table,
using data table method getColumnRange
then build each tick after a particular duration.
for instance, to display a tick every four hours along the axis, between the min and max dates...
const FOUR_HOURS = (1000 * 60 * 60 * 4);
var ticks = [];
var dateRange = $scope.data.getColumnRange(0);
for (var i = dateRange.min.getTime(); i <= dateRange.max.getTime(); i = i + FOUR_HOURS) {
var dateValue = new Date(i);
ticks.push({
v: dateValue,
f: dateFormatter.formatValue(dateValue)
});
}
I have a jqplot chart that has two lines. I'm using tooltipContentEditor to modify the text displayed in the tooltip. Prior to adding the second line, the tooltips displayed the values fine. Now that I have added another line, the tooltips always show the data for the second line (thus incorrectly for the first). How do I have the correct values displayed?
Blue line tooltip shows yellow line value:
Chart initialization (tooltipContentEditor function inside):
$.jqplot('mychart', [coords, paidCoords], {
//title:'Sales for ' + name,
animate: !$.jqplot.use_excanvas,
axesDefaults: {
tickRenderer: $.jqplot.CanvasAxisTickRenderer ,
},
axes:{
xaxis:{
renderer:$.jqplot.DateAxisRenderer,
tickInterval: tickInterval,
tickOptions: { formatString: formatString, angle: -30 }
},
yaxis:{
min:0
}
},
highlighter: {
show: true,
sizeAdjust: 7.5,
tooltipContentEditor: function(str, pointIndex, index, plot){
console.log('logging in tooltipeditor');
console.log(str);
console.log(pointIndex);
console.log(index);
console.log(plot);
var splitted = plot._plotData[1][index];
var x = splitted[0];
var y = splitted[1];
x = new Date(x);
x = x.toString();
return x + "<br/>$" + y;
}
},
cursor:{
show: true,
zoom:true,
showTooltip:false
}
});
Nevermind! I figured it out! In the tooltipContentEditor function I needed to do this:
var splitted = str.split(",");
Instead of this:
var splitted = plot._plotData[1][index];
I have googled and searched stack overflow alot before deciding to ask this question here because while some questions seemed similar, none of the solutions worked for me.
I have categorised data from sometimes a very wide range eg 1881 - 2012 and would like to dynamically calculate and divide this range into only 11 points on the x-axis which is what I have room to display with no zooming enabled. If data is from from 1881 - 2012, I would like it for example want it to show(not exactly calculated) from 1881, 1901, 1921, 1941.....if its from 1958 - 2013, then 1958, 1968, 1978....
xAxis: {
categories: range(start_year,end_year),
tickmarkPlacement: "on",
tickInterval: Math.ceil(range(start_year,end_year).length/11),
},
.......
function range(start_year,end_year)
{
var years = [];
for(var i = start_year;i<=end_year;i++)
{
years.push(i);
}
return years;
}
What this code does is just skip the labels for certain years but still plots the points on the graph leading to overlapping years on the x-axis.
Any leads on this would be highly appreciated.
Here's the fiddle illustrating my dilemma http://jsfiddle.net/YUPzk/4/
You can use tickPositioner and prepare your own calculating function.
The JavaScript below is one example of how this can be done:
Highcharts.chart('container', {
title: {
text: 'Custom tick positions'
},
subtitle: {
text: 'through axis.tickPositions and axis.tickPositioner'
},
xAxis: {
tickPositions: [0, 1, 2, 4, 8]
},
yAxis: {
tickPositioner: function () {
var positions = [],
tick = Math.floor(this.dataMin),
increment = Math.ceil((this.dataMax - this.dataMin) / 6);
if (this.dataMax !== null && this.dataMin !== null) {
for (tick; tick - increment <= this.dataMax; tick += increment) {
positions.push(tick);
}
}
return positions;
}
},
series: [{
data: [ [0, 1],[1, 3],[2, 2],[4, 4],[8, 3] ]
}]
});
The above demo code can be found at this jsfiddle
For anyone else struggling to get this done, here's what you need because i did lot's of googling with no answers to be found for people experiencing same challenge
Highcharts ; x-axis scaling issue
I am currently using highstock to plot the total number of items available based on time throughout the day (which then updates real-time).
If two changes to the total number of items happens at the same time, in highstock I get a vertical bar of the difference:
So in my example image we start with 4299 things, then 53 items are removed and 50 are added (technically at the same time, but are two different transactions and are two points). With a net difference of -3. (or in otherwords, I get {x: 5:44:15 and y: 4246, change: -53}, {x: 5:44:15, y: 4296, change: 50}).
So my question:
Is it possible in highstock to merge those points to get rid of the vertical bar and use 4296 as the shown value? I was hoping I could then use the tooltip formatter to loop through 'this.points' and display a change of -53 and a change of 50 in the tooltip so the user can see what resulted in a net change of -3.
If this is not possible, I will just merge the points myself and pass all the relevant information in the point to generate the tooltip (and chart look) that I am going for, but wanted to see if I could just utilize all the functionality of highstock first - and keep these points separate.
Thanks!
Edit::
new Highcharts.StockChart({
chart : {
renderTo : 'realTimeChart',
zoomType: 'x',
backgroundColor: '#feffdd',
style: {
fontFamily: 'Segoe UI'
},
type: 'spline'
},
plotOptions: {
area: { animation: false },
arearange: { animation: false },
areaspline: { animation: false },
areasplinerange: { animation: false },
bar: { animation: false },
column: { animation: false },
columnrange: { animation: false },
gauge: { animation: false },
line: { animation: false },
pie: { animation: false },
scatter: { animation: false },
series: { animation: false },
spline: { animation: false }
},
xAxis: {
ordinal: false
},
tooltip: {
animation: false,
formatter: function() {
var p = '';
p += '<span style="font-size: 9px;">' + Highcharts.dateFormat('%A, %b %e, %Y %H:%M:%S', this.x) +'</span><br/>';
$.each(this.points, function(i, point){
p += '<span style="color:' + this.series.color + '">' + this.series.name + '</span>: <b>'+ this.y +'</b>';
if (point.point.where) {
p += '<br />' + point.point.where + ' changed by ' + point.point.change + (point.point.who ? ' (' + point.point.who + ')' : '');
}
});
return p;
}
},
rangeSelector: {
buttons: [{
count: 30,
type: 'minute',
text: '30M'
}, {
count: 1,
type: 'hour',
text: '1H'
}, {
count: 6,
type: 'hour',
text: '6H'
}, {
type: 'all',
text: 'Day'
}],
inputEnabled: false,
selected: 1
},
exporting: {
enabled: false
},
series : [{
name : 'Available',
data : data,
lineWidth: 1,
states: {
hover: {
enabled: false
}
}
}]
Data is in the format I showed previously, except the x is actually in milliseconds since epoch:
data = [
{x: 123456789, y: 2000, where: 'Location', change: 40, who: 'Joe'},
{x: 123456789, y: 1960, where: 'Location', change: -40, who: 'Bob'},
...
];
Just wanted to follow up with how I easily got around the probem. Instead of placing by second, I decided to group points together to the nearest minute rounding down (so I have blocks of minutes).
Then for each point I passed in an array of the actual points contained within that minute block as a new argument, and updated the y value for that minute block. Then I used the tooltip formatter to display all the changes within that minute block with their actual time of change. This gave me a more flowing graph instead of all these hard vertical points for the same x-axis.
To easily change a data point at a specific x-axis point, I kept a separate array of the location of the minute block inside the series.data array for highcharts, that way if I needed to update a block, I knew exactly where that time series was.
Here is how I accomplished my task:
I create the reference array:
var pointIndex = {};
I created the inital data series from historical data for the day (pulled in via ajax):
var data = [];
var time = Math.floor(actual_time / 60000) * 60000;
pointIndex[time] = data.push({x: time, y: items_available, change: [{when: actual_time}]});
So actual_time is the number of milliseconds since epoch (when the even change occured), then I round that to the nearest minute to get the minute time block, change is the argument that will hold all the actual points for display in the tooltip.
So when I add a new point I check if the minute block exists, if it does not, add a new point, otherwise update an old point:
var time = (new Date()).getTime();
var point = Math.floor(time / 60000) * 60000;
if (pointIndex[point]) {
var change = chart.series[0].data[pointIndex[point]].change;
change.push({when: time});
chart.series[0].data[pointIndex[point]].update({x: point, y: items_available, change: change});
} else {
pointIndex[point] = chart.series[0].data.length;
chart.series[0].addPoint({x: point, y: items_available, change: [{when: time}]}, false, false);
}
(In all cases I do the actual chart refresh after I am done updating points.)
Hopefully that will help anyone else who finds theirself in the same position!
Edit:: (forgot the formatter):
tooltip: {
animation: false,
formatter: function() {
var p = '';
p += '<span style="font-size: 9px;">' + Highcharts.dateFormat('%A, %b %e, %Y %H:%M', this.x) +'</span><br/>';
$.each(this.points, function(i, point){
p += '<span style="color:' + this.series.color + '">' + this.series.name + '</span>: <b>'+ this.y +'</b>';
if (point.point.change) {
for(var j = 0; j < point.point.change.length; ++j) {
p += '<br />Change at: ' + new Date(point.point.change[j].when).toTimeString();
}
}
});
return p;
}
}