I'm trying to learn and use echarts.
I learned how to create a static chart using echarts and now I need to add data and series to my chart, dynamically.
There's methods like addSeries and addData in API but when I try to use these methods, there is some strange situation!
Assume that I have some radio channels that they had some programs in a period of time.
I don't know how many channels would be checked, so I have to get channel list from my database and then count programs per channel.
I tried this:
$.ajax({
type: 'POST',
url: "my url",
data: event,
error: function (jqXHR, textStatus, errorThrown) {
alert('ERROR');
},
beforeSend: function (xhr) {
$(document).find('.loaderWrapper').find('.loader').html('<img src="<?= base_url() ?>global/templates/default/desktop/assets/images/globe64.gif" width="76"><br /><span class="farsi">wait</span>');
},
success: function (data, textStatus, jqXHR) {
//console.log(JSON.parse(data), $.parseJSON(data));
var chartData = eval( $.parseJSON(data) );
if(data === 'eventError')
{
$(document).find('.loaderWrapper').find('.loader').html('<span class="alert alert-danger farsi">choose event</span>');
return false;
}//if eventError
if(data === 'dbError')
{
$(document).find('.loaderWrapper').find('.loader').html('<span class="alert alert-danger farsi">error</span>');
return false;
}//if eventError
var channelsArray = [];
for( var i=0; i < objSize(chartData.allChannels); i++ )
{
channelsArray.push(chartData.allChannels[i].channel);
}
console.log(channelsArray);
require(
[
'echarts',
'echarts/chart/bar', // require the specific chart type
'echarts/chart/line', // require the specific chart type
],
function (ec) {
// Initialize after dom ready
var myChart = ec.init(document.getElementById('programPerChannel'));
option = {
title : {
text: 'test title',
x : 'right'
},
tooltip : {
trigger: 'axis'
},
legend: {
data: channelsArray
},
toolbox: {
show : true,
x : 'left',
feature : {
mark : {
show: true,
title: {
mark : 'marker',
markUndo : 'undo',
markClear : 'clear'
},
lineStyle : {
width : 3,
color : '#1e90ff',
type : 'dashed'
}
},
dataView : {show: false, readOnly: false},
magicType : {show: true, type: ['line', 'bar']},
restore : {show: true},
saveAsImage : {show: true}
}
},
calculable : true,
xAxis : [
{
type : 'category',
boundaryGap : false,
data : channelsArray
}
],
yAxis : [
{
type : 'value'
}
]
};
// Load data into the ECharts instance
myChart.setOption(option);
for ( var j = 0; j < channelsArray.length; j++)
{
myChart.setSeries([
{
name:channelsArray[j],
type:'line',
stack: 'area',
symbol: 'none',
itemStyle: {
normal: {
areaStyle: {
color : (function (){
var zrColor = require('zrender/tool/color');
return zrColor.getLinearGradient(
0, 200, 0, 400,
[[0, 'rgba(128,' + 10 * j / 2 + ',0,0.8)'],[0.8, 'rgba(128,19,255,0.1)']]
)
})()
}
}
},
data:[
[j * 10, j * 11, j *3, j * 7],
]
}
]);//set series
//adding data inside addSeries will set data to first channel only, code was tested with or without this part
myChart.addData([
[1, 10 , j, j*2],
[1, 10 , j, j*2],
[1, 10 , j, j*2],
[1, 10 , j, j*2]
]);//add Data
}//for
}//functuin(ec)
);
$(document).find('.loaderWrapper').find('.loader').html('');
}//success
});//Ajax
With addSeries method, data will set to first channel only, and with addData echarts will show just flying bubble!!! :)
First situation image :
Second: bubbles!!!
Would you please help me to find out which part is my problem?
Thanks in advance
Reason for the first situation(just first channel will have data) is that setSeries method is not merging the series to the list of series, it is getting replaced. So you have to create/prepare a seriesList and then use setSeries method like this
var seriesList = [];
for ( var j = 0; j < channelsArray.length; j++)
{
seriesList.push(
{
name:channelsArray[j],
type:'line',
stack: 'area',
symbol: 'none',
itemStyle: {
normal: {
areaStyle: {
color : (function (){
var zrColor = require('zrender/tool/color');
return zrColor.getLinearGradient(
0, 200, 0, 400,
[[0, 'rgba(128,' + 10 * j / 2 + ',0,0.8)'],[0.8, 'rgba(128,19,255,0.1)']]
)
})()
}
}
},
data:[
[j * 10, j * 11, j *3, j * 7],
]
}
);//end-push
} //end-for-loop
myChart.setSeries(seriesList);
If you want an animated/moving dynamic graph then this demo will help.
Second: Bubbles !! is the default noDataLoadingOption animation of echarts. This can occur if there is no data loaded into the echart instance OR by breaking any options/configs passed or assigned to the echarts instance.
Related
I retrieved data using an AJAX call and data was received successfully as an array. But couldn't use them to draw a chart. When I tried to find the problem, I saw that when I call to the javascript function from the outside, it will respond and draw the chart but when I call it from the inside of the success part of AJAX, it will call to function but the chart will not draw. Not even through an error. (I used temporary data to check this trouble instead of retrieving data from the AJAX call [Var xData, Var yData].
Here is the sample data.
var xData = [
{ x: new Date(2020,11,11), y: 35.939 },
{ x: new Date(2020,11,12), y: 40.896 },
{ x: new Date(2020,11,15), y: null }
];
var yData = [
{ x: new Date(2020,11,11), y: 10 },
{ x: new Date(2020,11,12), y: 45.896 },
{ x: new Date(2020,11,13), y: null }
];
When I call like this, It will not respond.
function showDailySales()
{
var From = document.getElementById("dailySalesFrom").value;
var To = document.getElementById("dailySaleTo").value;
var select = document.getElementById('slctAllNamesForDaily');
var selectedItemID = select.options[select.selectedIndex].value;
$.ajax({
type: 'post',
url: 'controll/ReportController.php',
data: {
showDailySales : 1,
From : From,
To : To,
selectedItemID : selectedItemID,
},
success: function (response)
{
if (response) {
viewPoint(); // <- This call to function, but not draw the chart
}
}
});
}
When I call like this with above mentioned data, it will draw the chart.
function drawChart(){
var arr = new Array;
var chartLocationID = "chartContainerSales";
var chartTopic = "Chart Topic";
var xAxisTitle = "Date";
var yAxixTitle = "Qty";
arr.push(createData(xData, 'Apple'));
arr.push(createData(yData, 'Orange'));
chartShow(chartTopic, xAxisTitle, yAxixTitle, chartLocationID, arr);
}
function createData(getData, Name){
var j = {
type:"line",
axisYType: "primary",
name: Name,
showInLegend: true,
markerSize: 0,
connectNullData: true,
xValueType: "number",
lineThickness: 3,
dataPoints: getData,
};
return j;
}
function chartShow(chartTopic, xAxisTitle, yAxixTitle, chartLocationID, arr){
var chartLocationID = chartLocationID;
// alert("Function called");
window.onload = function() {
var chart = new CanvasJS.Chart(chartLocationID, {
animationEnabled: true,
title: {
text: chartTopic
},
axisX: {
title: xAxisTitle,
valueFormatString: "YYYY-MMM-D",
interval:1,
intervalType: "day"
},
axisY: {
title: yAxixTitle,
// minimum: 0,
suffix: "",
includeZero: true
},
legend: {
cursor: "pointer",
verticalAlign: "bottom",
horizontalAlign: "center",
dockInsidePlotArea: true,
},
data:arr
});
chart.render();
}
}
drawChart(); // <- This way call to function, and draw the chart
I tried so many ways, but it's not working. How do I fix this?
.
Changed the following code..
function chartShow(chartTopic, xAxisTitle, yAxixTitle, chartLocationID, arr){
var chartLocationID = chartLocationID;
// alert("Function called");
window.onload = function() { //something }
}
To :
function chartShow(chartTopic, xAxisTitle, yAxixTitle, chartLocationID, arr){
var chartLocationID = chartLocationID;
// alert("Function called");
show();
function show() { //something }
}
According to the first comment. That's worked.
EDIT: I have narrowed it down to something like this:
for (i = 0; i < data.length; i++) {
const newCanvas = document.createElement("canvas");
newCanvas.id = data[i].design_name;
const currentDiv = document.getElementById("chartSpace");
var parentDiv = document.getElementById("gridHere");
parentDiv.insertBefore(newCanvas, currentDiv);
createChart([data[i].design_name], [data[i].design_start, data[i].design_end]);
}
With the create chart making the chart id = to the array 'labels':
const myChart = new Chart(
document.getElementById(labels),
config
);
I am attempting to create a tool that creates an 'n' number of charts in ChartJS and save each of them as images. Currently, designButtonClick() sends the 'event_fky' value to
getDesigns(event_fky) in my controller. This method returns all designs with that foreign key. In turn, the chart plots each design on the chart. I need to evolve this into
something that can make a group individual charts for each design based on how many designs there are. My current solution, still conceptual, is to have methods in my controller
create chart variables 'chartData [data here]' and 'labels[datahere]' while looping through the designs returned from getDesigns, and sending those back to the JS script createChart
'n' number of times for each design. It would also send html chart/html element ids based on the design_name attribute to send back to createChart. This way, it is create a unique
chart 'n' number of times.
To save the charts as images, I would use the same set of element ids generated by getDesigns to send the charts to images using JS' toBase64Image() function and saving them to the
user's system.
Is this the best way of solving this problem? Or is this spaghetti, and is there a better method for this? My attempts to find better online answers have only resulted in docs on
updating one chart dynamically, not creating a dynamic number of charts. Much help is appreciated, code is below as well as a screenshot of the current chart output.
JavaScript:
var labels = [];
var cData = [];
function designButtonClick() {
var event_fky = 3;
$.ajax({
url: 'Tree/getDesigns',
type: 'POST',
data: { event_fky }
}).done(function (data) {
for (i = 0; i < data.length; i++) {
labels.push(data[i].design_name);
cData.push([data[i].design_start, data[i].design_end])
}
createChart(labels, cData);
});
}
function createChart(labels, cData) {
const data = {
labels: labels,
datasets: [{
barThickness: 2,
categoryPercentage: .5,
label: 'Design Time',
data: cData,
backgroundColor: [
'rgba(255, 26, 104, 0.2)'
],
borderColor: [
'rgba(255, 26, 104, 1)'
],
borderWidth: 1,
borderSkipped: false,
borderRadius: 20
}]
};
const config = {
type: 'bar',
data,
options: {
indexAxis: 'y',
scales: {
y: {
beginAtZero: true
},
x: {
min: 0,
max: 6000,
ticks: {
stepSize: 1000
}
}
}
}
};
const myChart = new Chart(
document.getElementById('myChart'),
config
);
}
C# Controller:
public ActionResult getDesigns(int? event_fky)
{
var designs = from e in _context.designs
where (event_fky.HasValue ? e.event_fky == event_fky : e.event_fky == null)
select new
{
design_pky = e.design_pky,
design_name = e.design_name,
design_start = e.design_start,
design_end = e.design_end
};
return this.Json(designs, JsonRequestBehavior.AllowGet);
}
Designs Table:
--------Design--------
design_pky |int
event_fky |int
design_name |varchar
design_start |number
design_end |number
Screenshot of Chart
This is a working answer for the javascript:
var eventList = function () {
var tmp = null;
$.ajax({
'async': false,
url: 'Tree/getEventIDs',
type: 'POST',
data: {},
'success': function (data) {
tmp = data;
}
});
return tmp;
}();
for (var i = 0; i < eventList.length; i++) {
event_fky = eventList[i].event_pky;
event_name = eventList[i].event_name;
event_length = eventList[i].event_end;
var designList = function () {
var tmpi = null;
$.ajax({
'async': false,
url: 'Tree/getDesigns',
type: 'POST',
data: {event_fky},
'success': function (data1) {
tmpi = data1;
}
});
console.log(event_fky);
console.log(tmpi);
return tmpi;
}();
var dLabels = [];
var dLengths = [];
for (var j = 0; j < designList.length; j++) {
dLabels.push(designList[j].design_name);
dLengths.push([designList[j].design_start, designList[j].design_end]);
}
const newCanvas = document.createElement("canvas");
newCanvas.id = event_name;
const currentDiv = document.getElementById("chartSpace");
var parentDiv = document.getElementById("gridHere");
parentDiv.insertBefore(newCanvas, currentDiv);
if (dLabels.length != 0) {
createChart(dLabels, dLengths, event_name, event_length);
}
}
}
function createChart(labels, cData, evName, evLen) {
// setup
const data = {
labels: labels,
datasets: [{
barThickness: 4,
categoryPercentage: .5,
label: evName,
data: cData,
backgroundColor: [
'rgba(' + Math.random() * 85 + ', ' + Math.random() * 170 + ', ' + Math.random() * 255 + ', 1)'
],
borderColor: [
'rgba(255, 26, 104, 1)'
],
borderWidth: 0,
borderSkipped: false,
borderRadius: 20
}]
};
// config
const config = {
type: 'bar',
data,
options: {
indexAxis: 'y',
scales: {
y: {
beginAtZero: true
},
x: {
min: 0,
max: evLen,
ticks: {
stepSize: 100
}
}
}
}
};
// render init block
const myChart = new Chart(
document.getElementById(evName),
config
);
return myChart;
}
I'm trying to use yahoo finance data to generate a Highcharts candlestick chart like this http://www.highcharts.com/stock/demo/candlestick-and-volume. But I keep getting this error: http://www.highcharts.com/errors/15
Highcharts Error #15
Highcharts expects data to be sorted
This happens when you are trying to create a line series or a stock chart where the data is not sorted in ascending X order. For performance reasons, Highcharts does not sort the data, instead it is required that the implementer pre-sorts the data.
My code is as follows.
$(function () {
$.getJSON('http://websitescraper.heroku.com/?url=http://ichart.finance.yahoo.com/table.csv?s=000338.sz&callback=?', function (csvdata) {
//console.log(csvdata);
var arr = csvdata.split('\n').slice(1);
var data = [];
for (var i = arr.length-1; i >= 0; --i) {
//console.log(arr[i]);
var line = arr[i].split(',');
line[0] = Date.parse(line[0]);
line = $.map(line, function(v) {
return parseFloat(v);
});
line = line.slice(0,6);
//var j = JSON.stringify(line.slice(0,0+6));
console.log(line);
data.push(line);
}
data = JSON.stringify(data.slice(1));
console.log(data);
run(data);
});
});
function run(data) {
// split the data set into ohlc and volume
var ohlc = [],
volume = [],
dataLength = data.length,
// set the allowed units for data grouping
/*groupingUnits = [[
'week', // unit name
[1] // allowed multiples
], [
'month',
[1, 2, 3, 4, 6]
]],*/
i = 0;
for (i; i < dataLength; i += 1) {
ohlc.push([
data[i][0], // the date
data[i][1], // open
data[i][2], // high
data[i][3], // low
data[i][4] // close
]);
volume.push([
data[i][0], // the date
data[i][5] // the volume
]);
}
// create the chart
$('#container2').highcharts('StockChart', {
rangeSelector: {
selected: 1
},
title: {
text: 'Shanghai Composite Index Historical'
},
yAxis: [{
labels: {
align: 'right',
x: -3
},
title: {
text: 'OHLC'
},
height: '60%',
lineWidth: 2
}, {
labels: {
align: 'right',
x: -3
},
title: {
text: 'Volume'
},
top: '65%',
height: '35%',
offset: 0,
lineWidth: 2
}],
series: [{
type: 'candlestick',
upLineColor: 'red',
downLineColor: 'green',
name: 'SSE',
data: ohlc,
/*dataGrouping: {
units: groupingUnits
}*/
}, {
type: 'column',
name: 'Volume',
data: volume,
yAxis: 1
/*dataGrouping: {
units: groupingUnits
}*/
}]
});
}
Can somebody help? Thanks a lot!
The problem is the data = JSON.stringify(data.slice(1));. It turns the array to a string, therefore Highstock doesn't recognize it. Remove JSON.stringify and it will work fine:
data = data.slice(1);
Here's the DEMO.
I am working on drawing graphs from our data. There is no problem on drawing graph basically. The only problem is that the flags information is not loaded and located on the graph lines. Let me give you the issues on it.
Data cannot be brought to the graph if it has over 900 numbers of items.
One data might have more than 4000 items.
Instead using one big data, I tried to spilt one data into small pieces of data. Each piece of data has 800 items, and they are intended being loaded on the graph sequentially. However, this process was not easily done well. Sometime the graph module cannot load every piece exactly. Moreover, this process take much time than using one data.
I wonder whether an appropriate way to load flag data which contains many items exits or not.
$(function() {
var report_data;
$.ajax({
type: "post",
url:"/apps.chart/chart.reportShort",
data:"callback=?&report_type=CO&business_code=005930",
dataType:"json",
success:function(report){
report_data = report;
}
});
$.getJSON('/apps.chart/chart.chartList?callback=?&report_type=CO&business_code=005930', function(data) {
// Create the chart
$('#chartView').highcharts('StockChart', {
chart: {
plotBorderColor: '#346691',
plotBorderWidth: 2,
height: 600
},
rangeSelector : {
inputEnabled: $('#chartView').width() > 400,
selected : 1
},
title : {
text : 'SK'
},
tooltip: {
type: 'datetime',
dateTimeLabelFormats: {
day: '%Y년 %m월 %d일'
},
style: {
width: '300px'
},
valueDecimals: 0
},
xAxis: {
type: 'datetime',
dateTimeLabelFormats: {
day: '%m.%d',
week: '%m.%d',
month: '%Y.%m',
year: '%Y'
}
},
yAxis : {
labels : {
formatter : function() {
var n = this.value;
var reg = /(^[+-]?\d+)(\d{3})/;
n += '';
while (reg.test(n))
n = n.replace(reg, '$1' + ',' + '$2');
return n;
},
align : 'left',
x : 5,
y : 2
},
maxPadding: 0.2,
minPadding: 0,
minorTickInterval: 'auto',
title : {
text : '금액(원)',
}
},
series : [{
name : '종가',
data : data,
id : 'dataseries',
marker : {
enabled : true,
radius : 3
},
shadow : true,
tooltip : {
valueDecimals : 0
}
}
// the event marker flags
,{
type : 'flags',
data : report_data,
style: {
cursor: 'hand'
},
onSeries : 'dataseries',
shape : 'circlepin',
width : 15,
height : 15,
color : '#121212'
}
]
});
});
});
Probably the problem is that you call two ajax asynchronously, so I advice you to next ajax insiae first callback and then initialise chart.
I currently have a working chart using this:
$.ajax({
type: "GET",
url: graphURL,
data: "",
cache: false,
success: function (response) {
//alert(response);
jsonData = JSON.parse(response);
if(jsonData != '' && jsonData != null) {
var category = jsonData.XData.split(",");
var series = jsonData.YData.split(",");
series = $.each(series, function (i, amt) {
series[i] = parseFloat(series[i]);
});
//Display chart
UserChart(series, category, jsonData.YAxisTitle);
}
}
});
... but it doesn't allow me to set options like if I wanted an area chart instead of line etc.. how do I modify the code so I could include something like the following which I see in all examples:
chart : {
renderTo : 'container'
},
rangeSelector : {
selected : 1
},
title : {
text : 'AAPL Stock Price'
},
series : [{
name : 'AAPL Stock Price',
data : data,
type : 'area',
threshold : null,
tooltip : {
valueDecimals : 2
},
fillColor : {
linearGradient : {
x1: 0,
y1: 0,
x2: 0,
y2: 1
},
stops : [[0, Highcharts.getOptions().colors[0]], [1, 'rgba(0,0,0,0)']]
}
}]
The problem is that series is the series points' data.
Try to pass a serie to UserChart.
Like the following:
var points = jsonData.YData.split(",");
points = $.each(series, function (i, amt) {
points[i] = parseFloat(series[i]);
});
// here you set your serie config
var serie = {
name: 'AAPL Stock Price',
type: 'area',
data: points,
threshold : null,
tooltip : {
valueDecimals : 2
}
};
UserChart(serie, category, jsonData.YAxisTitle);
Then in UserChart you should add the serie directly to the chart series.
Example:
var options: {
chart : {
renderTo : 'container'
},
rangeSelector : {
selected : 1
},
title : {
text : 'AAPL Stock Price'
},
series : []
};
function UserChart(serie, category, jsonData.YAxisTitle) {
options.series.push(serie);
// add your code
}