Goodmorning everyone,
I'm trying to implement the "candlestick" graph from the Chartjs library;
I took the example shown in the documentation as a guide and everything works correctly: the 'x' and 'y' axes show the correct values;
The problem is that the actual graph is not displayed, even if the Cartesian axes are present
this is the code used for implement Chart and the result image
var barCount = 5;
var initialDateStr = '01 Apr 2017 00:00 Z';
var barData = getRandomData(initialDateStr, barCount);
//console.log(barData)
//function lineData() { return barData.map(d => { return { x: d.x, y: d.c} }) };
var chart = new Chart(ctx, {
type: 'candlestick',
data: {
datasets: [{
label: 'CHRT - Chart.js Corporation',
data: barData
}]
}
});
var getRandomInt = function(max) {
return Math.floor(Math.random() * Math.floor(max));
};
function randomNumber(min, max) {
return Math.random() * (max - min) + min;
}
function randomBar(date, lastClose) {
var open = +randomNumber(lastClose * 0.95, lastClose * 1.05).toFixed(2);
var close = +randomNumber(open * 0.95, open * 1.05).toFixed(2);
var high = +randomNumber(Math.max(open, close), Math.max(open, close) * 1.1).toFixed(2);
var low = +randomNumber(Math.min(open, close) * 0.9, Math.min(open, close)).toFixed(2);
return {
x: date.valueOf(),
o: open,
h: high,
l: low,
c: close
};
}
Related
I'm making a pretty simple Stacked Bar Chart using C3.js. https://c3js.org/samples/chart_bar_stacked.html . I want to be able to append text to the values, specifically +"Mb" so that my end users know that those numbers indicate Megabits, and not anything else. I've added a useful y-axis label, but for my question, I'm wondering how to utilize d3.js to perform the formatting of the values.
I used d3.format which suggests I am able to append string, but haven't figured how to pull it off.
var chart = c3.generate({
data: {
columns: [
['data1', 2000, 2000, 4000],
['data2', 1300, 1000, 500],
['data3', 2000, 2000, 2500]
],
type: 'bar',
groups: [
['data1', 'data2']
]
},
axis: {
y: {
label: {
text: 'Bandwidth Usage (Mb)',
position: 'outer-middle'
},
tick: {
format: d3.format('') + "mb"
}
}
}
});
This isn't working as expected, I'm getting "TypeError: this.tickFormat is not a function". Wondering if there are any c3 gurus out there than can help me with this problem? Thanks!!
You could write a tick function as follows:
tick:
{
format: function (d) {
if ((d) > 0) {
d = d + "Mb";
}
return d;
}
},
I have used >0 for d, so the "Mb" just gets appended to d if d is not 0, but you could also extend the code to automatically transform the input:
tick:
{
format: function (d) {
if (((d / 1000) >= 1) & ((d / 1000) < 1000)) {
d = Math.round((d / 1000 )*100) / 100 + "Kb";
}
else if ((d / 1000000) >= 1) {
d = Math.round((d / 1000000 )*100) / 100 + "Mb";
}
return d;
}
},
By doing this, you could use untransformed data as input, and it will transform it to either "Kb" or "Mb" unit. 100 sets 2 digits after decimal point.
I made a line chart using Chart.js version 2.1.3.
var canvas = $('#gold_chart').get(0);
var ctx = canvas.getContext('2d');
var fillPatternGold = ctx.createLinearGradient(0, 0, 0, canvas.height);
fillPatternGold.addColorStop(0, '#fdca55');
fillPatternGold.addColorStop(1, '#ffffff');
var goldChart = new Chart(ctx, {
type: 'line',
animation: false,
data: {
labels: dates,
datasets: [{
label: '',
data: prices,
pointRadius: 0,
borderWidth: 1,
borderColor: '#a97f35',
backgroundColor: fillPatternGold
}]
},
title: {
position: 'bottom',
text: '\u7F8E\u5143 / \u76CE\u53F8'
},
options: {
legend: {
display: false
},
tooltips: {
callback: function(tooltipItem) {
return tooltipItem.yLabel;
}
},
scales: {
xAxes: [{
ticks: {
maxTicksLimit: 8
}
}]
}
}
});
The output is as follow:
As you can see, I limited the maximum count of ticks to 8 via maxTicksLimit. However, the distribution is not even. How can I make the ticks distribute evenly?
p.s. there are always 289 records in the dataset, and the data is recorded every 5 minutes. Sample values of prices variable are:
[
{"14:10", 1280.3},
{"14:15", 1280.25},
{"14:20", 1282.85}
]
I tried different values of maxTicksLimit, and the results are still not distributed evenly.
Chart.js uses an integral skipRatio (to figure out how many labels to skip). With Chart.js v2.1.x, you can write your own plugin to use a fractional skipRatio
Preview
Script
Chart.pluginService.register({
afterUpdate: function (chart) {
var xScale = chart.scales['x-axis-0'];
if (xScale.options.ticks.maxTicksLimit) {
// store the original maxTicksLimit
xScale.options.ticks._maxTicksLimit = xScale.options.ticks.maxTicksLimit;
// let chart.js draw the first and last label
xScale.options.ticks.maxTicksLimit = (xScale.ticks.length % xScale.options.ticks._maxTicksLimit === 0) ? 1 : 2;
var originalXScaleDraw = xScale.draw
xScale.draw = function () {
originalXScaleDraw.apply(this, arguments);
var xScale = chart.scales['x-axis-0'];
if (xScale.options.ticks.maxTicksLimit) {
var helpers = Chart.helpers;
var tickFontColor = helpers.getValueOrDefault(xScale.options.ticks.fontColor, Chart.defaults.global.defaultFontColor);
var tickFontSize = helpers.getValueOrDefault(xScale.options.ticks.fontSize, Chart.defaults.global.defaultFontSize);
var tickFontStyle = helpers.getValueOrDefault(xScale.options.ticks.fontStyle, Chart.defaults.global.defaultFontStyle);
var tickFontFamily = helpers.getValueOrDefault(xScale.options.ticks.fontFamily, Chart.defaults.global.defaultFontFamily);
var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
var tl = xScale.options.gridLines.tickMarkLength;
var isRotated = xScale.labelRotation !== 0;
var yTickStart = xScale.top;
var yTickEnd = xScale.top + tl;
var chartArea = chart.chartArea;
// use the saved ticks
var maxTicks = xScale.options.ticks._maxTicksLimit - 1;
var ticksPerVisibleTick = xScale.ticks.length / maxTicks;
// chart.js uses an integral skipRatio - this causes all the fractional ticks to be accounted for between the last 2 labels
// we use a fractional skipRatio
var ticksCovered = 0;
helpers.each(xScale.ticks, function (label, index) {
if (index < ticksCovered)
return;
ticksCovered += ticksPerVisibleTick;
// chart.js has already drawn these 2
if (index === 0 || index === (xScale.ticks.length - 1))
return;
// copy of chart.js code
var xLineValue = this.getPixelForTick(index);
var xLabelValue = this.getPixelForTick(index, this.options.gridLines.offsetGridLines);
if (this.options.gridLines.display) {
this.ctx.lineWidth = this.options.gridLines.lineWidth;
this.ctx.strokeStyle = this.options.gridLines.color;
xLineValue += helpers.aliasPixel(this.ctx.lineWidth);
// Draw the label area
this.ctx.beginPath();
if (this.options.gridLines.drawTicks) {
this.ctx.moveTo(xLineValue, yTickStart);
this.ctx.lineTo(xLineValue, yTickEnd);
}
// Draw the chart area
if (this.options.gridLines.drawOnChartArea) {
this.ctx.moveTo(xLineValue, chartArea.top);
this.ctx.lineTo(xLineValue, chartArea.bottom);
}
// Need to stroke in the loop because we are potentially changing line widths & colours
this.ctx.stroke();
}
if (this.options.ticks.display) {
this.ctx.save();
this.ctx.translate(xLabelValue + this.options.ticks.labelOffset, (isRotated) ? this.top + 12 : this.options.position === "top" ? this.bottom - tl : this.top + tl);
this.ctx.rotate(helpers.toRadians(this.labelRotation) * -1);
this.ctx.font = tickLabelFont;
this.ctx.textAlign = (isRotated) ? "right" : "center";
this.ctx.textBaseline = (isRotated) ? "middle" : this.options.position === "top" ? "bottom" : "top";
this.ctx.fillText(label, 0, 0);
this.ctx.restore();
}
}, xScale);
}
};
}
},
});
Fiddle - http://jsfiddle.net/bh63pe1v/
A simpler solution until this is permanently fixed by the Chart JS contributors is to include a decimal in maxTicksLimit.
For example:
maxTicksLimit: 8,
produces a huge gap at the end.
maxTicksLimit: 8.1,
Does not produce a huge gap at the end.
Depending on what you want to set your maxTicksLimit to, you need to play around with different decimals to see which one produces the best result.
Just do this:
yAxes: [{
ticks: {
stepSize: Math.round((Math.max.apply(Math, myListOfyValues) / 10)/5)*5,
beginAtZero: true,
precision: 0
}
}]
10 = the number of ticks
5 = rounds tick values to the nearest 5 - all y values will be incremented evenly
Similar will work for xAxes too.
I have been trying to make highchart tooltip to show the nearest point incase the x-axis value aren't align in different series.
This is what I got so far
http://jsfiddle.net/Yw8hb/5/
Highcharts.wrap(Highcharts.Tooltip.prototype, 'refresh', function (proceed) {
var args = arguments,
points = args[1],
point = points[0],
chart = point.series.chart;
// Loop over all the series of the chart
Highcharts.each(chart.series, function(series) {
// This one already exist
if (series == point.series) return;
var current,
dist,
distance = Number.MAX_VALUE;
// Loop over all the points
Highcharts.each(series.points, function(p) {
// use the distance in X to determine the closest point
dist = Math.abs(p.x - point.x);
if (dist < distance) {
distance = dist;
current = p;
}
});
// Add the closest point to the array
points.push(current);
});
proceed.apply(this, [].slice.call(args, 1));
});
It seems to be working half way there however when you hover in some spot it shows duplicated series. I have spent hours trying to figure this out any help would be very appreciated.
Before insertion, check whether points array contains the current point in your refresh callback function.
// Add the closest point to the array
if(points.indexOf(current)==-1)
points.push(current);
Highcharts.wrap(Highcharts.Tooltip.prototype, 'refresh', function (proceed) {
var args = arguments,
points = args[1],
point = points[0],
chart = point.series.chart;
// Loop over all the series of the chart
Highcharts.each(chart.series, function(series) {
// This one already exist
if (series == point.series) return;
var current,
dist,
distance = Number.MAX_VALUE;
// Loop over all the points
Highcharts.each(series.points, function(p) {
// use the distance in X to determine the closest point
dist = Math.abs(p.x - point.x);
if (dist < distance) {
distance = dist;
current = p;
}
});
// Add the closest point to the array
if(points.indexOf(current)==-1)
points.push(current);
});
proceed.apply(this, [].slice.call(args, 1));
});
$('#container').highcharts({
tooltip: {
shared: true
},
xAxis: {
crosshair: {
color: '#F70000'
}
},
series: [{
data: [{
x: 0.0,
y: 1
}, {
x: 1.0,
y: 2
}, {
x: 2.0,
y: 3
}, {
x: 3.0,
y: 2
}, {
x: 4.0,
y: 1
}]
}, {
data: [{
x: 0.2,
y: 0
}, {
x: 1.2,
y: 1
}, {
x: 2.2,
y: 1
}, {
x: 3.2,
y: 1
}, {
x: 4.2,
y: 2
}]
}, {
data: [{
x: 0.2,
y: 5
}, {
x: 1.2,
y: 9
}, {
x: 2.2,
y: 4
}, {
x: 3.2,
y: 5
}, {
x: 4.2,
y: 3
}]
}]
});
#container {
min-width: 300px;
max-width: 800px;
height: 300px;
margin: 1em auto;
}
<script src="http://code.jquery.com/jquery-git.js"></script>
<script src="http://code.highcharts.com/highcharts.js"></script>
<script src="http://code.highcharts.com/modules/exporting.js"></script>
<div id="container"></div>
If you want to show visible series' in the tooltip only, change
// This one already exist
if (series == point.series) return;
to
// This one already exist
if (series == point.series || series.visible==false) return;
Thanks for you solution!!!
for constant order the tooltips
Highcharts.wrap(Highcharts.Tooltip.prototype, 'refresh', function (proceed) {
var args = arguments,
points = args[1],
point = points[0],
chart = point.series.chart;
// Loop over all the series of the chart
Highcharts.each(chart.series, function (series) {
// This one already exist
if (series == point.series || series.visible == false)
return;
var current,
dist,
distance = Number.MAX_VALUE;
// Loop over all the points
Highcharts.each(series.points, function (p) {
// use the distance in X to determine the closest point
dist = Math.abs(p.x - point.x);
if (dist < distance) {
distance = dist;
current = p;
return;
}
});
// Add the closest point to the array
if (points.indexOf(current) == -1)
points.push(current);
});
// for not changing the tooltip series order
var tt = [].slice.call(args, 1);
tt[0].sort(function (a, b) {
if (a.color < b.color)
return -1;
if (a.color > b.color)
return 1;
return 0;
});
proceed.apply(this, tt);
});
Don't forget tooltip option shared!
options = {
tooltip: {
shared: true,
....
I have a flotr2 graph that is supposed to display some data in it:
Edit: Chrome users: Please note, this jsfiddle does not seem to work me in chrome, not sure why
http://jsfiddle.net/1jgb2k6r/7/
My problem is that my bars aren't center-aligned over the date they represent.
$(function () {
(function color_gradients(container) {
var myData = [];
myData.push([new Date('1/1/2014').getTime(), Math.ceil(Math.random() * 10)]);
myData.push([new Date('1/8/2014').getTime(), Math.ceil(Math.random() * 10)]);
myData.push([new Date('1/15/2014').getTime(), Math.ceil(Math.random() * 10)]);
myData.push([new Date('1/22/2014').getTime(), Math.ceil(Math.random() * 10)]);
var
bars = {
data: myData,
bars: {
show: true,
barWidth: 0.8,
lineWidth: 20,
}
};
graph = Flotr.draw(
container, [bars], {
yaxis: {
min: 0,
max: 11
},
xaxis: {
mode: 'time',
timeMode: 'local',
min: (new Date('1/1/2014').getTime() - (7 * 24 * 60 * 60 * 1000)),
max: (new Date('1/22/2014').getTime() + (7 * 24 * 60 * 60 * 1000))
}
});
})(document.getElementById("editor-render-0"));
});
Is there a way in flot to realign these bars?
Always in the eleventh hour I post my question, and always in the eleventh hour I find the solution.
http://jsfiddle.net/1jgb2k6r/10/
Basically, the flot line of graphing libraries have an integral bug in their datetime calculations. They all suffer severe floating point rounding errors in relation to time axis on the graph which causes the bars to shimmy over to the left of their intended plot locations.
Here is an example of a getTimeScale function that returns a date as a week number from epoch time (starting at ~2200 from 1/1/2014):
function getTimeScale(date){
return (new Date(date).getTime() / 1000 / 60 / 60 / 24 / 7);
}
This function, applied to date arguments in the data series, returns a normalized number not on the order of the hundreds of thousands:
$(function () {
(function color_gradients(container) {
var myData = [];
myData.push([getTimeScale('1/1/2014'),Math.ceil(Math.random() * 10)]);
myData.push([getTimeScale('1/8/2014'),Math.ceil(Math.random() * 10)]);
myData.push([getTimeScale('1/15/2014'), Math.ceil(Math.random() * 10)]);
myData.push([getTimeScale('1/22/2014'), Math.ceil(Math.random() * 10)]);
var bars = {
data: myData,
bars: {
show: true,
barWidth: 0.8,
lineWidth: 1,
}
};
graph = Flotr.draw(
container, [bars], {
yaxis: {
min: 0,
max: 11
},
xaxis: {
ticks: ticks,
min: (getTimeScale('1/1/2014') - 1),
max: (getTimeScale('1/22/2014') + 1)
}
});
})(document.getElementById("editor-render-0"));
});
In order to display the ticks as a datetime again, you have to specify the ticks explicitly:
var ticks = [];
ticks.push([getTimeScale('1/1/2014'), '1/1/2014']);
ticks.push([getTimeScale('1/8/2014'), '1/8/2014']);
ticks.push([getTimeScale('1/15/2014'), '1/15/2014']);
ticks.push([getTimeScale('1/22/2014'), '1/22/2014']);
graph = Flotr.draw(
container, [bars], {
yaxis: {
min: 0,
max: 11
},
xaxis: {
ticks: ticks,
min: (getTimeScale('1/1/2014') - 1), //gives a little padding room on the graph
max: (getTimeScale('1/22/2014') + 1) //gives a little padding room on the graph
}
});
I am trying to create realtime linechart based on notifcations that i am getting. I have looked in examples - http://jsfiddle.net/kaliatech/4TMMD/
however i cannot make it work, the rate is being updated and data is inserted into the array, but i am getting in the plot only one point which is always the first one
this.data = [{
key: "New Incidents Rate",
values: getData()
}];
function getData() {
return that.newIncidentRate;
}
redraw();
function redraw() {
nv.addGraph(function() {
that.chart = nv.models.lineChart()
.x(function(d) { return d.x })
.y(function(d) { return d.y });
//.color(d3.scale.category10().range());
that.chart.xAxis
.axisLabel('Time')
.tickFormat(function(d) {
return d3.time.format('%x')(new Date(d))
});
that.chart.yAxis
.axisLabel('Rate')
.tickFormat(d3.format(',r'));
d3.select('#chart svg')
.datum(that.data)
//.transition().duration(500)
.call(that.chart);
nv.utils.windowResize(that.chart.update);
return that.chart;
});
}
amplify.subscribe("newIncident", function (rate) {
$scope.newRate = rate;
$scope.$apply();
if (that.newIncidentRate.length > 20) {
that.newIncidentRate.shift();
}
var currentTime = new Date();
that.newIncidentRate.push({
x: new Date(currentTime.getTime()),
y: rate
});
redraw();
});
i have tested it more and it seems to be related to the number of points, when i take the sample all is ok (adding 30 point - fresh every time) same when adding 2 points , so i tested to keep all the points in container and always copy it to new one - falied, it seems there is a problem to increase the number of points
function getData() {
var arr = [];
var theDate = new Date(2012, 01, 01, 0, 0, 0, 0);
newIncidentRate.push({x: new Date(theDate.getTime()), y: Math.random() * 100});
theDate.setDate(theDate.getDate() + moveDate);
++moveDate;
for (var d in newIncidentRate) {
arr.push(d) ;
}
return arr;
}
full code - http://jsfiddle.net/lirazrom/Wx5bG/