I need to create real-time values per minute chart, its last bar value is incremented for a minute after its addition and after a minute the new bar with value 1 is added and first bar (the oldest one) is removed.
I need all these changes to be animated like in the example below.
var chart = AmCharts.makeChart("chartdiv", {
"type": "serial",
"theme": "light",
"dataProvider": generateChartData(),
"graphs": [{
"valueField": "value",
"type": "column",
"fillAlphas": 1,
"alphaField": "alpha1"
}, {
"valueField": "value2",
"fillAlphas": 1,
"type": "column",
"alphaField": "alpha2"
}],
"valueAxes": [{
"minimum": 0,
"maximum": 400
}],
"chartCursor": {},
"categoryAxis": {
"parseDates": true,
"minPeriod": "mm"
},
"zoomOutOnDataUpdate": false,
"categoryField": "date"
});
function generateChartData() {
var chartData = [];
var firstDate = new Date( 2012, 0, 1 );
firstDate.setDate( firstDate.getDate() - 1000 );
firstDate.setHours( 0, Math.floor(Math.random() * 10), 0, 0 );
for ( var i = 0; i < 10; i++ ) {
var newDate = new Date( firstDate );
newDate.setHours( 0, i, 0, 0 );
var a = Math.round( Math.random() * ( 200 + i ) ) + 100 + i;
var b = Math.round( Math.random() * ( 200 + i ) ) + 100 + i;
chartData.push( {
date: newDate,
value: a,
value2: b,
alpha1: (Math.random() < 0.5 ? 0 : 1),
alpha2: (Math.random() < 0.5 ? 0 : 1)
} );
}
return chartData;
}
function loop() {
var data = generateChartData();
chart.animateData(data, {
duration: 1000,
complete: function () {
setTimeout(loop, 2000);
}
});
}
chart.addListener("init", function () {
setTimeout(loop, 1000);
});
html, body {
width: 100%;
height: 100%;
margin: 0px;
}
#chartdiv {
width: 100%;
height: 100%;
}
<script src="https://www.amcharts.com/lib/3/amcharts.js"></script>
<script src="https://www.amcharts.com/lib/3/serial.js"></script>
<script src="https://www.amcharts.com/lib/3/themes/light.js"></script>
<script src="https://www.amcharts.com/lib/3/plugins/animate/animate.min.js"></script>
<div id="chartdiv"></div>
I set up the codepen with bar chart and its data update here. I use animateData() method like in the example, but no animation is applied to the chart. What am I doing wrong?
The data needs to be a completely new array for the animation to work, as indicated in the example you linked. You'll need to clone your modified array and pass that into the animateData method. Here's an example using lodash's cloneDeep method
chart.animateData(_.cloneDeep(data), {
duration: 1000,
complete: () => setTimeout(updateChartData, 500),
});
Updated codepen
Related
QUESTION:
I have created a line chart using C3.js that is updating its values every 2 seconds.
To achieve this goal i use the following code.
UPDATE:
I have noticed that this is happening when i'm swapping from a tab to another of the bowser.Let's say chart is on TAB1 and i swap to TAB2 for 5 minutes, when i get back to TAB1 i have lost some label as you can see in screenshots below and time on the X-axis is 5 minutes later too...
UPDATE2
The problem is related to setInterval almost for sure.
function drawChart1()
{
var chart = c3.generate({
bindto: '#chart11',
//Size of the chart
size: {
height: 250,
width: 952
},
data: {
x: 'x',
columns: []
},
//Color of the lines
color: {
pattern: [ '#ff9896', '#9edae5']
},
axis: {
x: {
//show: false,
label: 'Time',
type: 'timeseries',
tick: {
format: '%H:%M:%S',
//format: '%H:%M',
}
},
y: {
//http://c3js.org/samples/axes_label_position.html
label: 'Kbps'
},
},
legend: {
//position: 'right'
}
});
var chartObj = {
"chart": chart,
"redrawArgs": {},
"truncThreshold": undefined
}
var date = Date.now();
var timeInc = 2000;
//Update values every x seconds
var interval = setInterval(function () {
var dataCols = [];
date = date + timeInc;
var minX = date - 10000;
var maxX = date;
var redrawArgs = chartObj.redrawArgs;
if (!chartObj.truncThreshold) {
chartObj.truncThreshold = maxX;
} else if (minX > chartObj.truncThreshold) {
redrawArgs.length = 1;
} else {
redrawArgs.length = 0;
}
chartObj.chart.axis.range({max: {x: maxX}, min: {x: minX}});
redrawArgs.duration = 0;
var TraficDown1 = 1 + Math.floor(Math.random() * 1000);
var TraficUp1 = 1 + Math.floor(Math.random() * 1000);
dataCols.push(['x', date]);
dataCols.push(['Kbps UP', TraficUp1]);
dataCols.push(['Kbps DOWN', TraficDown1])
redrawArgs.columns = dataCols;
chartObj.chart.flow(redrawArgs);
}, 2000);
}
For some time the code is working as intended:
Problem is that after some minutes I got this strange behaviour, where values are missing on x axis.
Someone can give me some hints? I'm using c3.js and D3.js v3.
found it on here https://c3js.org/reference.html#axis-x-tick-culling
axis: {
x: {
culling: false, // <-- THIS!
//show: false,
label: 'Time',
type: 'timeseries',
tick: {
format: '%H:%M:%S',
//format: '%H:%M',
}
},
I have a pie chart integration in Spotfire which works well when the data is in a simpler format in 'data' and 'columns'. The data binds to the chart properly (this is the kind of format i've seen in most demos).
However in other real-life usages in Spotfire, the JSON which is produced is differently formatted and ceases to draw a pie chart properly. I think it should be possible to adjust the script to bind to this data format, but i don't know how?
In my fiddle it is working with simpler data format, if commenting this out and uncommenting the other data the failed chart can be seen...
https://jsfiddle.net/paulsmithleadershipfactor/3k2gzuw0/
The full code is here also...
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE9"/>
<title>JS Visualization Tester with Highcharts</title>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://code.highcharts.com/highcharts.js"></script>
<script>
var chart; // Global chart object used to determine whether Highcharts has been intialized
var color = null;
var pie = null;
var svg = null;
var path = null;
function renderCore(sfdata)
{
if (resizing) {
return;
}
// Extract the columns
var columns = sfdata.columns;
columns.shift();
// Extract the data array section
var chartdata = sfdata.data;
// count the marked rows in the data set, needed later for marking rendering logic
var markedRows = 0;
for (var i = 0; i < chartdata.length; i++)
{
if (chartdata[i].hints.marked)
{
markedRows = markedRows + 1;
}
}
var width = window.innerWidth;
var height = window.innerHeight;
var radius = Math.min(width, height) / 2;
if ( !chart )
{
$('#js_chart').highcharts({
chart: {
plotBackgroundColor: '#f1f2f2',
plotBorderWidth: null,
plotShadow: false,
type: 'pie'
},
title: {
text: 'Pie',
},
tooltip: {
formatter: function() {
var sliceIndex = this.point.index;
var sliceName = this.series.chart.axes[0].categories[sliceIndex];
return sliceName + ':' +
'<b>' + this.y + '</b>';
}
},
plotOptions: {
pie: {
allowPointSelect: true,
cursor: 'pointer',
showInLegend: true,
depth: 35,
innerSize: 100,
dataLabels: {
enabled: true,
format: '{point.y:,.0f}'
}
}
},
legend: {
enabled: true,
labelFormatter: function() {
var legendIndex = this.index;
var legendName = this.series.chart.axes[0].categories[legendIndex];
return legendName;
}
},
xAxis: {
categories: columns
}
});
}
chart = $('#js_chart').highcharts();
for ( var nIndex = 0 ; nIndex < chartdata.length ; nIndex++ )
{
var row = chartdata[nIndex];
// Check for an existing chart data series with the current id
var series = chart.get ( row.items[0] );
var seriesData = [];
for (var c = 1; c < row.items.length; c++) {
seriesData.push(Number(row.items[c]));
}
if ( series != null )
{
// Update the existing series with the new data
series.update ( {
data: seriesData
}, false );
}
else
{
// Create a new series
chart.addSeries ( {
id: row.items[0],
name: row.items[0],
data: seriesData
}, false );
}
}
for ( nSeriesIndex = 0 ; nSeriesIndex < chart.series.length ; nSeriesIndex++ )
{
var series = chart.series[nSeriesIndex];
var found = false;
for ( nDataIndex = 0 ; nDataIndex < chartdata.length ; nDataIndex++ )
{
var row = chartdata[nDataIndex];
if ( series.name == row.items[0] )
{
found = true;
break;
}
}
if ( found != true )
{
series.remove ( false );
nSeriesIndex = 0;
}
}
chart.redraw ();
wait ( sfdata.wait, sfdata.static );
}
var resizing = false;
window.onresize = function (event) {
resizing = true;
if ($("#js_chart")) {
}
resizing = false;
}
</script>
</head>
<body>
<button style="position:absolute; z-index:99" type="button" onclick="call_renderCore()">Call renderCore</button>
<div id="js_chart"></div>
<script type="text/javascript">
function call_renderCore()
{
var sfdata =
{
"columns": ["Sales (Total)", "Marketing (Total)", "Development (Total)", "Customer Support (Total)", "IT (Total)", "Administration (Total)"],
/* comment out the 'columns' above and uncomment 'columns' below */
/* "columns": [
"count([lastcontact])",
"First([lastcontact])"
], */
/* uncomment above and comment below */
"data": [{"items": [93000, 58000, 102000, 66000, 43000, 24000], "hints": {"index": 0}}]
/* comment out the 'data' above and uncomment 'data' below */
/* "data": [
{
"items": [
131,
"3 – 6 months"
],
"hints": {
"index": 0
}
},
{
"items": [
78,
"6 months – 1 year"
],
"hints": {
"index": 1
}
},
{
"items": [
89,
"Can't remember"
],
"hints": {
"index": 2
}
},
{
"items": [
56,
"Over a year ago"
],
"hints": {
"index": 4
}
},
{
"items": [
442,
"Less than 3 months"
],
"hints": {
"index": 3
}
}
], */
}
renderCore ( sfdata );
display_data ( sfdata );
}
</script>
</body>
</html>
im trying to add a line to amcharts like this image :
its avaliable in amcharts but only on hover ,i want to add it so it will be always there without hover , i tried to Use Am charts Guides as following :
"guides": [{
"valueField":"high",
"value": this part need to be dynamic,
"lineAlpha": 1,
"lineThickness": 1,
"lineColor": "#fff",
"label": 'high'
}],
this way the line is working but it needs a value to be drawn ,the value i want it to be dynamic and changes with SERVER SENT EVENTS ,
You can add the guide after you get your server event and/or change it depending on how your app is structured. Any additions or changes to the chart properties or data require that you call the chart instance's validateData() method.
For adding a new guide (assuming regular serial chart with the guides inserted at the top level of the config):
chartInstance.guides.push({
"value": /*your value here*/,
"lineAlpha": 1,
"lineThickness": 1,
"lineColor": "#fff",
"label": 'high'
});
chartInstance.validateData();
For updating a guide (assuming you're updating the first guide in the array):
//assuming you're updating the first guide in the array:
chartInstance.guides[0].value = /*new value*/
chartInstance.validateData();
Note that guides don't have a valueField. You can see a full list of properties here.
Here's a demo that adds a guide and then randomly changes the guide position every three seconds to demonstrate:
function generateChartData() {
var firstDate = new Date();
var chartData = [];
firstDate.setHours(0, 0, 0, 0);
firstDate.setDate(firstDate.getDate() - 30);
for (var i = 0; i < 30; i++) {
var newDate = new Date(firstDate);
newDate.setDate(newDate.getDate() + i);
var open = Math.round(Math.random() * 30 + 100);
var close = open + Math.round(Math.random() * 15 - Math.random() * 10);
var low;
if (open < close) {
low = open - Math.round(Math.random() * 5);
} else {
low = close - Math.round(Math.random() * 5);
}
var high;
if (open < close) {
high = close + Math.round(Math.random() * 5);
} else {
high = open + Math.round(Math.random() * 5);
}
var value = Math.round(Math.random() * 30 + 100);
chartData[i] = {
date: newDate,
open: open,
close: close,
high: high,
low: low,
value: value
};
}
return chartData;
}
var chart = AmCharts.makeChart("chartdiv", {
type: "serial",
theme: "black",
dataProvider: generateChartData(),
valueAxes: [
{
position: "right"
}
],
graphs: [
{
type: "candlestick",
valueField: "value",
openField: "open",
highField: "high",
lowField: "low",
closeField: "close",
lineColor: "#7f8da9",
fillColors: "#7f8da9",
negativeLineColor: "#db4c3c",
negativeFillColors: "#db4c3c",
fillAlphas: 1
}
],
categoryField: "date",
categoryAxis: {
parseDates: true,
equalSpacing: true
}
});
//simulating events using a 3 second interval
setInterval(function() {
if (chart.guides.length) {
chart.guides[0].value = Math.floor(Math.random() * 30 + 100);
} else {
chart.guides.push({
value: Math.floor(Math.random() * 30 + 100),
lineAlpha: 1,
lineThickness: 1,
lineColor: "#fff",
inside: true,
label: "high"
});
}
chart.validateData();
}, 3000);
#chartdiv {
width: 100%;
height: 300px;
background: #0d0d0d;
}
<script src="//www.amcharts.com/lib/3/amcharts.js"></script>
<script src="//www.amcharts.com/lib/3/serial.js"></script>
<script src="//www.amcharts.com/lib/3/themes/black.js"></script>
<div id="chartdiv"></div>
I'm trying to implement this demo in my app but I get whole map black regardless of values. here is my code:
var map = AmCharts.makeChart( "chartdiv", {
"type": "map",
"theme": "light",
"colorSteps": 10,
"dataProvider": {
"mapURL": "https://www.amcharts.com//lib/3/maps/svg/iranLow.svg",
"getAreasFromMap": true,
"zoomLevel": 0.9,
"areas": []
},
"areasSettings": {
"autoZoom": true,
"balloonText": "[[title]]: <strong>[[value]]</strong>"
},
"valueLegend": {
"right": 10,
"minValue": "little",
"maxValue": "a lot!"
},
"zoomControl": {
"minZoomLevel": 0.9
},
"titles": 'titles',
"listeners": [ {
"event": "init",
"method": updateHeatmap
} ]
} );
function updateHeatmap( event ) {
var map = event.chart;
if ( map.dataGenerated )
return;
if ( map.dataProvider.areas.length === 0 ) {
setTimeout( updateHeatmap, 100 );
return;
}
for ( var i = 0; i < map.dataProvider.areas.length; i++ ) {
map.dataProvider.areas[ i ].value = Math.round( Math.random() * 10000 );
}
map.dataGenerated = true;
map.validateNow();
}
is there anything else that I should do in order to get color range based on values?
You're likely running into cross origin issues when using the SVG on AmCharts' site as your source when running your code locally (especially if your local environment is http - the include is https).
Ideally you should be using the map JavaScript instead of the SVG file. Try including the iranLow.js map javascript file and use map: "iranLow" instead of mapURL: 'path/to/svg'.
iranLow.js include (after ammap.js):
<script type="text/javascript" src="https://www.amcharts.com/lib/3/maps/js/iranLow.js"></script>
Modified dataprovider definition
"dataProvider": {
"map": "iranLow",
"getAreasFromMap": true,
"zoomLevel": 0.9,
"areas": []
},
Demo below:
var map = AmCharts.makeChart( "chartdiv", {
"type": "map",
"theme": "light",
"colorSteps": 10,
"dataProvider": {
"map": "iranLow",
"getAreasFromMap": true,
"zoomLevel": 0.9,
"areas": []
},
"areasSettings": {
"autoZoom": true,
"balloonText": "[[title]]: <strong>[[value]]</strong>"
},
"valueLegend": {
"right": 10,
"minValue": "little",
"maxValue": "a lot!"
},
"zoomControl": {
"minZoomLevel": 0.9
},
"titles": 'titles',
"listeners": [ {
"event": "init",
"method": updateHeatmap
} ]
} );
function updateHeatmap( event ) {
var map = event.chart;
if ( map.dataGenerated )
return;
if ( map.dataProvider.areas.length === 0 ) {
setTimeout( updateHeatmap, 100 );
return;
}
for ( var i = 0; i < map.dataProvider.areas.length; i++ ) {
map.dataProvider.areas[ i ].value = Math.round( Math.random() * 10000 );
}
map.dataGenerated = true;
map.validateNow();
}
#chartdiv {
width: 100%;
height: 300px;
}
<script type="text/javascript" src="https://www.amcharts.com/lib/3/ammap.js"></script>
<script type="text/javascript" src="https://www.amcharts.com/lib/3/themes/light.js"></script>
<script type="text/javascript" src="https://www.amcharts.com/lib/3/maps/js/iranLow.js"></script>
<div id="chartdiv"></div>
From the example here, I kind of know how to create a Flot graph that shows tooltips when hovering. But the examples only show to how to display tooltips containing the x value, y value, label, etc., and I can't figure out how to create more customized tooltips.
Is there someplace I can attach custom data, that I can access when creating the tooltip?
For example, to simplify, let's suppose my code looks like:
var d = [ { label: "Fake!", data: [ [1290802154, 0.3], [1292502155, 0.1] ] } ];
var options = {
xaxis: { mode: "time" },
series: {
lines: { show: true },
points: { show: true }
},
grid: { hoverable: true, clickable: true }
};
$.plot($("#placeholder"), d, options);
Now, when clicking on the first datapoint, I want the tooltip to show "Hello", and when clicking on the second datapoint I want to show "Bye". How do I do this? When binding the plothover event
$(".placeholder").bind("plothover", function (event, pos, item) { /* etc. */ };
I'm not sure what "item" refers to, and how to attach data to it.
You can add data to the series simply by adding it to the data array.
Instead of
$.plot(element, [[1, 2], [2, 4]] ...)
You can
$.plot(element, [[1, 2, "label"], [2, 4, "another label"]] ...)
And then use that information to show a custom label.
See this fiddle for a full example:
http://jsfiddle.net/UtcBK/328/
$(function() {
var sin = [],
cos = [];
for (var i = 0; i < 14; i += 0.5) {
sin.push([i, Math.sin(i), 'some custom label ' + i]);
cos.push([i, Math.cos(i), 'another custom label ' + i]);
}
var plot = $.plot($("#placeholder"), [{
data: sin,
label: "sin(x)"
},
{
data: cos,
label: "cos(x)"
}
], {
series: {
lines: {
show: true
},
points: {
show: true
}
},
grid: {
hoverable: true,
clickable: true
},
yaxis: {
min: -1.2,
max: 1.2
}
});
$("#placeholder").bind("plothover", function(event, pos, item) {
$("#tooltip").remove();
if (item) {
var tooltip = item.series.data[item.dataIndex][2];
$('<div id="tooltip">' + tooltip + '</div>')
.css({
position: 'absolute',
display: 'none',
top: item.pageY + 5,
left: item.pageX + 5,
border: '1px solid #fdd',
padding: '2px',
'background-color': '#fee',
opacity: 0.80
})
.appendTo("body").fadeIn(200);
showTooltip(item.pageX, item.pageY, tooltip);
}
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="http://people.iola.dk/olau/flot/jquery.flot.js"></script>
<div id="placeholder" style="width:600px;height:300px"></div>
Here is a rough JSFiddle example that I whipped up. Not sure if it's functioning exactly how you like but might spark an idea...
[update]
you'll want to bind to the
$("#placeholder").bind("plotclick", function (event, pos, item) {/* code */});
event for clicking events
[update2] Updated Example
I've adjusted the example to use your test data and to work more with what you have described above. As for the item object it seems that it is generated on the fly so, from what I can tell, you can not add additional data to it. However, you can create an array to cache the item objects when clicked and add additional data to them and use them for the hover event.
This new example does just that, it shows the normal tooltip when nothing is clicked. but once clicked it determines if the point clicked was the first or second and adds an addition property to the item object called alternateText and stores it in an array called itemsClicked.
Now when a point is hovered over it checks to see if there is a cached item object within the array based on the same index of the current item object, which is obtained via item.dataIndex. If there is a matching index in the cache array itemsClicked it will grab the item object from it and use the alternateText property that was added during the click event earlier.
The first point's item object would look something like this:
item : {
dataIndex: 0,
datapoint: [
1290802154,
0.3
],
pageX: 38,
pageY: 82,
series: {/* properties of the series that this point is in */},
seriesIndex: 0
}
Once clicked it would then look like this and be stored in the itemsClicked array:
item : {
alternateText: 'hello',
dataIndex: 0,
datapoint: [
1290802154,
0.3
],
pageX: 38,
pageY: 82,
series: {/* properties of the series that this point is in */},
seriesIndex: 0
}
Please let me know if any of this is helpful or not, if not I'll shut up and stop updating my answer :P
Also you can try following code:
function showTooltip(x, y, contents, z) {
$('<div id="tooltip">' + contents + '</div>').css({
position: 'absolute',
display: 'none',
top: y - 30,
left: x - 110,
'font-weight': 'bold',
border: '1px solid rgb(255, 221, 221)',
padding: '2px',
'background-color': z,
opacity: '0.8'
}).appendTo("body").show();
};
$(document).ready(
$(function() {
var data = [{
"label": "scott",
"data": [
[1317427200000 - 5000000 * 3, "17017"],
[1317513600000 - 5000000 * 5, "77260"]
]
},
{
"label": "martin",
"data": [
[1317427200000 - 5000000 * 2, "96113"],
[1317513600000 - 5000000 * 4, "33407"]
]
},
{
"label": "solonio",
"data": [
[1317427200000 - 5000000, "13041"],
[1317513600000 - 5000000 * 3, "82943"]
]
},
{
"label": "swarowsky",
"data": [
[1317427200000, "83479"],
[1317513600000 - 5000000 * 2, "96357"],
[1317600000000 - 5000000, "55431"]
]
},
{
"label": "elvis",
"data": [
[1317427200000 + 5000000, "40114"],
[1317513600000 - 5000000 * 1, "47065"]
]
},
{
"label": "alan",
"data": [
[1317427200000 + 5000000 * 2, "82504"],
[1317513600000, "46577"]
]
},
{
"label": "tony",
"data": [
[1317513600000 + 5000000, "88967"]
]
},
{
"label": "bill",
"data": [
[1317513600000 + 5000000 * 2, "60187"],
[1317600000000, "39090"]
]
},
{
"label": "tim",
"data": [
[1317513600000 + 5000000 * 3, "95382"],
[1317600000000 + 5000000, "89699"]
]
},
{
"label": "britney",
"data": [
[1317513600000 + 5000000 * 4, "76772"]
]
},
{
"label": "logan",
"data": [
[1317513600000 + 5000000 * 5, "88674"]
]
}
];
var options = {
series: {
bars: {
show: true,
barWidth: 60 * 60 * 1000,
align: 'center'
}
},
points: {
show: true
},
lines: {
show: true
},
grid: {
hoverable: true,
clickable: true
},
yaxes: {
min: 0
},
xaxis: {
mode: 'time',
timeformat: "%b %d",
minTickSize: [1, "month"],
tickSize: [1, "day"],
autoscaleMargin: .10
}
};
$(function() {
$.plot($('#placeholder'), data, options);
});
$(function() {
var previousPoint = null;
$("#placeholder").bind("plothover", function(event, pos, item) {
if (item) {
if (previousPoint != item.datapoint) {
previousPoint = item.datapoint;
$("#tooltip").remove();
var x = item.datapoint[0],
y = item.datapoint[1] - item.datapoint[2];
debugger;
showTooltip(item.pageX, item.pageY, y + " " + item.series.label, item.series.color);
}
} else {
$("#tooltip").remove();
previousPoint = null;
}
})
});
})
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.4.1/jquery.min.js"></script>
<script src="http://people.iola.dk/olau/flot/jquery.flot.js"></script>
<div id="content">
<div class="demo-container">
<div id="placeholder" class="demo-placeholder" style="width:800px;height:600px;"></div>
</div>
</div>