Amcharts' stockchart : lines in javascript too simple versus old flash version - javascript

I have to update an old amcharts' stockchart in the flash version to the javascript version. I made it, but the result is too different for my client.
The flash version :
The new javascript version :
As you can see, the graph's lines on the new version are really too simple compare to the old version, there isn't enough details.
This is the code of the new graph :
var chart = new AmCharts.AmStockChart();
chart.dataSets = dataSets;
var stockPanel = new AmCharts.StockPanel();
stockPanel.showCategoryAxis = true;
stockPanel.numberFormatter = {precision:2, decimalSeparator:',', thousandsSeparator:' '};
stockPanel.percentFormatter = {precision:2, decimalSeparator:',', thousandsSeparator:' '};
var graph = new AmCharts.StockGraph();
graph.valueField = 'value';
graph.openField = 'open';
graph.closeField = 'close';
graph.comparable = true;
graph.type = 'line';
graph.minDistance = 0;
graph.noStepRisers = true;
graph.clustered = false;
stockPanel.addStockGraph(graph);
var stockLegend = new AmCharts.StockLegend();
stockLegend.markerType = 'bubble';
stockLegend.markerSize = 8;
stockLegend.periodValueText = '[[value.close]]';
stockLegend.valueTextComparing = '[[value]] | [[percents.value]]%';
stockLegend.periodValueTextComparing = '[[value.close]] | [[percents.value.close]]%';
stockLegend.horizontalGap = 1;
stockLegend.spacing = 100;
stockPanel.stockLegend = stockLegend;
chart.panels = [ stockPanel ];
var categoryAxesSettings = new AmCharts.CategoryAxesSettings();
chart.categoryAxesSettings = categoryAxesSettings;
var scrollbarSettings = new AmCharts.ChartScrollbarSettings();
scrollbarSettings.color = '#000';
scrollbarSettings.gridColor = '#fff';
scrollbarSettings.backgroundColor = '#fff';
scrollbarSettings.gridColor = '#fff';
scrollbarSettings.graphFillColor = '#F5F5F5'; //jsonData.funds.color;
scrollbarSettings.selectedGraphFillColor = '#CCDDE9';
scrollbarSettings.selectedBackgroundColor = '#fff'
scrollbarSettings.graph = graph;
scrollbarSettings.graphType = 'line';
scrollbarSettings.usePeriod = "MM";
chart.chartScrollbarSettings = scrollbarSettings;
var periodSelector = new AmCharts.PeriodSelector();
periodSelector.position = 'top';
periodSelector.fromText = '';
periodSelector.toText = ' - ';
periodSelector.periodsText = '';
periodSelector.dateFormat = 'DD/MM/YYYY';
periodSelector.periods = [
{ period: 'MM', count: 1, label: '1M' },
{ period: 'MM', count: 3, label: '3M' },
{ period: 'YYYY', count: 1, label: '1Y' },
{ period: 'YYYY', count: 3, label: '3Y' },
{ period: 'YYYY', count: 5, label: '5Y' },
{ period: 'YTD', label: 'YTD' },
{ period: 'MAX', label: 'MAX' }
];
chart.periodSelector = periodSelector;
chart.write('fund_historic');
Which parameter do I have to add or change ?
Thanks

OK I found it.
When I compared the flash settings file, I see the max_series parameter fixed to 300, the double of the default value of the javascript version.
So this is the solution :
var categoryAxesSettings = new AmCharts.CategoryAxesSettings();
categoryAxesSettings.maxSeries = 300;
chart.categoryAxesSettings = categoryAxesSettings;

Related

User memory limit exceeded in GEE

I started using GEE recently. I wanted to apply the non-parametric Mann-Kendall method to a collection of Sentinel-1 images. However there is a memory error in the code and this is my problem. Link code:https://code.earthengine.google.com/4bf1dbbcd116c8e3f5c584fbb45e8c19
var quadricula = ee.Geometry.Polygon([[-51.20732599322313,-15.60026586116677],[-51.20732599322313,-15.455379154650098],[-51.262600956602036,-15.455379154650098],[-51.262600956602036,-15.60026586116677]]);
//Map.addLayer(quadricula);
// Filter collection to dates of interest.
var Sentinel1 = ee.ImageCollection('COPERNICUS/S1_GRD')
.filterDate('2015-01-01', '2022-01-01')
.filterBounds(quadricula);
print('Quantidade de imagens no intervalo definido:',Sentinel1.size());
//===========================================================================//
var wrapper = require('users/adugnagirma/gee_s1_ard:wrapper');
var helper = require('users/adugnagirma/gee_s1_ard:utilities');
//---------------------------------------------------------------------------//
// DEFINE PARAMETERS
//---------------------------------------------------------------------------//
//var geometry = ee.Geometry.Polygon([[-51.20732599322313,-15.60026586116677],[-51.20732599322313,-15.455379154650098],[-51.262600956602036,-15.455379154650098],[-51.262600956602036,-15.60026586116677]]);
//Map.addLayer(geometry);
var parameter = {//1. Data Selection
START_DATE: "2015-01-01",
STOP_DATE: "2022-01-01",
POLARIZATION:'VVVH',
ORBIT : 'BOTH',
GEOMETRY: quadricula, //uncomment if interactively selecting a region of interest
//GEOMETRY: ee.Geometry.Polygon([[[104.80, 11.61],[104.80, 11.36],[105.16, 11.36],[105.16, 11.61]]], null, false), //Uncomment if providing coordinates
//GEOMETRY: ee.Geometry.Polygon([[[112.05, -0.25],[112.05, -0.45],[112.25, -0.45],[112.25, -0.25]]], null, false),
//2. Additional Border noise correction
APPLY_ADDITIONAL_BORDER_NOISE_CORRECTION: true,
//3.Speckle filter
APPLY_SPECKLE_FILTERING: true,
SPECKLE_FILTER_FRAMEWORK: 'MULTI',
SPECKLE_FILTER: 'REFINED LEE',
SPECKLE_FILTER_KERNEL_SIZE: 5,
SPECKLE_FILTER_NR_OF_IMAGES: 10,
//4. Radiometric terrain normalization
APPLY_TERRAIN_FLATTENING: true,
DEM: ee.Image('NASA/NASADEM_HGT/001'),
TERRAIN_FLATTENING_MODEL: 'VOLUME',
TERRAIN_FLATTENING_ADDITIONAL_LAYOVER_SHADOW_BUFFER: 0,
//5. Output
FORMAT : 'DB',
CLIP_TO_ROI: true,
SAVE_ASSETS: false
};
//---------------------------------------------------------------------------//
// DO THE JOB
//---------------------------------------------------------------------------//
//Preprocess the S1 collection
var s1_preprocces = wrapper.s1_preproc(parameter);
var s1 = s1_preprocces[0];
s1_preprocces = s1_preprocces[1];
//---------------------------------------------------------------------------//
// VISUALIZE
//---------------------------------------------------------------------------//
//Visulaization of the first image in the collection in RGB for VV, VH, images
var visparam = {};
if (parameter.POLARIZATION=='VVVH'){
if (parameter.FORMAT=='DB'){
var s1_preprocces_view = s1_preprocces.map(helper.add_ratio_lin).map(helper.lin_to_db2);
var s1_view = s1.map(helper.add_ratio_lin).map(helper.lin_to_db2);
visparam = {bands:['VV','VH','VVVH_ratio'],min: [-20, -25, 1],max: [0, -5, 15]};
}
else {
var s1_preprocces_view = s1_preprocces.map(helper.add_ratio_lin);
var s1_view = s1.map(helper.add_ratio_lin);
visparam = {bands:['VV','VH','VVVH_ratio'], min: [0.01, 0.0032, 1.25],max: [1, 0.31, 31.62]};
}
}
else {
if (parameter.FORMAT=='DB') {
s1_preprocces_view = s1_preprocces.map(helper.lin_to_db);
s1_view = s1.map(helper.lin_to_db);
visparam = {bands:[parameter.POLARIZATION],min: -25,max: 0} ;
}
else {
s1_preprocces_view = s1_preprocces;
s1_view = s1;
visparam = {bands:[parameter.POLARIZATION],min: 0,max: 0.2};
}
}
// Calcula o MRFDI
var MRFDI = s1_preprocces.map(function(image) {
return image.normalizedDifference(['VV', 'VH']).rename('MRFDI');
});
var addMRFDI = function (image) {
var MRFDI = image.expression('(VV - VH)/(VV + VH)', {
'VV' : image.select('VV'),
'VH' : image.select('VH'),
}).float();
return image.addBands(MRFDI.rename('MRFDI'));
};
var MRFDI = s1_preprocces.map(addMRFDI)
.select('MRFDI');
// Calcula o RVI
var RVI = s1_preprocces.map(function(image) {
return image.normalizedDifference(['VV', 'VH']).rename('RVI');
});
var addRVI = function (image) {
var RVI = image.expression('(4*VH)/(VV + VH)', {
'VV' : image.select('VV'),
'VH' : image.select('VH'),
}).float();
return image.addBands(RVI.rename('RVI'));
};
var RVI = s1_preprocces.map(addRVI)
.select('RVI');
Map.centerObject(parameter.GEOMETRY, 12);
Map.addLayer(s1_view.first(), visparam, 'First image in the input S1 collection', true);
Map.addLayer(s1_preprocces_view.first(), visparam, 'First image in the processed S1 collection', true);
//---------------------------------------------------------------------------//
// EXPORT
//---------------------------------------------------------------------------//
//Convert format for export
if (parameter.FORMAT=='DB'){
s1_preprocces = s1_preprocces.map(helper.lin_to_db);
}
//Save processed collection to asset
if(parameter.SAVE_ASSETS) {
helper.Download.ImageCollection.toAsset(s1_preprocces, '',
{scale: 10,
region: s1_preprocces.geometry(),
type: 'float'});
}
print('Quantidade de imagens s1_preprocces:',s1_preprocces.size());
// Define an image collection time series to chart, MODIS vegetation indices
// in this case.
var imgCol = s1_preprocces
.select(['VV', 'VH']);
//=================================================================================//
//mann-kendall
var coll = s1_preprocces.map(function(image) {
return image.select().addBands(image.normalizedDifference(['VH', 'VV']));
})
.filter(ee.Filter.calendarRange(8, 9, 'month'));
Map.addLayer(coll, {}, 'coll');
var afterFilter = ee.Filter.lessThan({
leftField: 'system:time_start',
rightField: 'system:time_start'
});
var joined = ee.ImageCollection(ee.Join.saveAll('after').apply({
primary: coll,
secondary: coll,
condition: afterFilter
}));
var sign = function(i, j) { // i and j are images
return ee.Image(j).neq(i) // Zero case
.multiply(ee.Image(j).subtract(i).clamp(-1, 1)).int();
};
var kendall = ee.ImageCollection(joined.map(function(current) {
var afterCollection = ee.ImageCollection.fromImages(current.get('after'));
return afterCollection.map(function(image) {
// The unmask is to prevent accumulation of masked pixels that
// result from the undefined case of when either current or image
// is masked. It won't affect the sum, since it's unmasked to zero.
return ee.Image(sign(current, image)).unmask(0);
});
// Set parallelScale to avoid User memory limit exceeded.
}).flatten()).reduce('sum', 2);
var palette_sens = {min: -0.001, max: 0.001, palette: [
'red', 'white', 'green']};
var palette = ['red', 'white', 'green'];
// Stretch this as necessary.g
Map.addLayer(kendall, {palette: palette}, 'kendall');
var slope = function(i, j) { // i and j are images
return ee.Image(j).subtract(i)
.divide(ee.Image(j).date().difference(ee.Image(i).date(), 'days'))
.rename('slope')
.float();
};
var slopes = ee.ImageCollection(joined.map(function(current) {
var afterCollection = ee.ImageCollection.fromImages(current.get('after'));
return afterCollection.map(function(image) {
return ee.Image(slope(current, image));
});
}).flatten());
var sensSlope = slopes.reduce(ee.Reducer.median(), 2); // Set parallelScale.
Map.addLayer(sensSlope, palette_sens, 'sensSlope');
var epochDate = ee.Date('1970-01-01');
var sensIntercept = coll.map(function(image) {
var epochDays = image.date().difference(epochDate, 'days').float();
return image.subtract(sensSlope.multiply(epochDays)).float();
}).reduce(ee.Reducer.median(), 2);
Map.addLayer(sensIntercept, {}, 'sensIntercept');
Map.addLayer(table, {color: 'blue'});
I hope to resolve this impasse without changing the adopted time interval and reducing the size of the study area.

How do I update data in a SmoothieChart without spamming the the second value in TimeSeries?

I want to build some kind of stock trading website using JSON
function getJSON(_url, _type) {
var request = new XMLHttpRequest();
request.open("GET", _url, true);
request.onload = function () {
if (request.status == 200 && request.readyState == 4) {
Callback(JSON.parse(request.responseText), _type);
} else
error("reach");
};
request.send(null);
}
and a callback function to get data.
function Callback(_data, _type) {
switch (_type) {
case 1.1:
initAktien(_data);
break;
case 1.2:
updateAktien(_data);
break;
}
}
In order to visualize the stocks I'm using SmoothieChart.
var lines = [];
var smoothie = new SmoothieChart({
tooltip: true, millisPerPixel: 50, minValueScale: 0,
grid: {fillStyle: '#99d6ff', strokeStyle: '#6c7d82', verticalSections: 8, borderVisible: false},
labels: {disabled: true, fontSize: 9}, minValue: 0, horizontalLines: [{color: '#000000', lineWidth: 1, value: 0}, {color: '#4d0f00', lineWidth: 2, value: 100}]
});
smoothie.streamTo(document.getElementById("aktien-grafik"), 500);
A little heads up in case you're wondering: "Aktien" means stocks, "preis" means price, and "anzahlVerfügbar" means available amount. I got this code from someone else and I'm supposed to "fix" it. That's why it's partially in German.
function initAktien(_data) {
var stocks = document.getElementById("aktien");
var amount = document.getElementById("amount");
for (var i = 0; i <= _data.length - 1; i++) {
var stockoption = document.createElement("option");
stockoption.innerHTML = _data[i].name;
stocks.appendChild(stockoption);
}
for (var j = 1; j <= 5; j++) {
var amountoption = document.createElement("option");
amountoption.innerHTML = j;
amount.appendChild(amountoption);
}
}
function updateAktien(_data) {
var select = document.getElementById("aktien").selectedIndex;
document.getElementById("kurs").innerHTML = _data[select].preis;
document.getElementById("anz").innerHTML = _data[select].anzahlVerfuegbar;
//spam is because of here
lines.push(new TimeSeries());
lines[select].append(new Date().getTime(), _data[select].preis);
smoothie.addTimeSeries(lines[select], {lineWidth: 2, strokeStyle: "#000000"});
}
I can render the graph just fine and I even get an overlay displaying the timestamp and the and the price and the time. Unfortunately it spams the price in the overlay which slows the site down quite fast. Removing the overlay doesn't solve the problem since the price is appended nonstop
somewhere at the point I marked. I don't know how to keep the data updating and the chart drawing without spamming that value.
I don't think it's the best solution, but it works. It's somewhat redundant code.
function initAktien(_data) {
var stocks = getID("aktien");
var amount = getID("amount");
for (var i = 0; i <= _data.length - 1; i++) {
var stockoption = document.createElement("option");
stockoption.innerHTML = _data[i].name;
stocks.appendChild(stockoption);
lines.push(new TimeSeries());
lines[i].append(new Date().getTime(), _data[i].preis.toFixed(2));
smoothie.addTimeSeries(lines[i], {lineWidth: 2, strokeStyle: "#000000"});
}
}
function updateAktien(_data) {
var select = getID("aktien").selectedIndex;
getID("kurs").innerHTML = _data[select].preis.toFixed(2);
getID("anz").innerHTML = _data[select].anzahlVerfuegbar;
//Grafik update
lines[select].append(new Date().getTime(), _data[select].preis.toFixed(2));
}

setting google chart multiple axes from dynamically generated data

I'm trying to replicate the following code from a working example:
series: {0: {targetAxisIndex:0},
1: {targetAxisIndex:0},
2: {targetAxisIndex:1},
This is for setting which y-axis is used to plot different columns from a dataTable on a Google chart.
However I have a variable number of columns (based on user input), therefore am collecting an array of the required axis (the axisAssignment Array in the below example).
My code is below:
var series = {};
for (i=0;i<axisAssignment.length;i++)
{
series[i] = {targetAxisIndex: axisAssignment[i]};
}
return series;
However, all of my data is only being written to the left axis, despite the debugger suggesting that the object is correct. My option code is below:
var options =
{
hAxis: {title: xTitle},
vAxes: {0: {title: y1Type},
1: {title: y2Type}
},
series: calculateSeries(),
pointSize: 1,
legend: {position: 'top', textStyle: {fontSize: 10}}
};
Any assistance would be greatly apreciated.
Thanks
Tom
edit: whole file for reference (it's a work in progress so a bit of a mess I'm afraid)
google.load("visualization", "1", {packages:["corechart"]});
google.setOnLoadCallback(drawChart());
function drawChart()
{
var title = "Node: "+currentNode;
var xTitle = "Date";
var yTitle = titles[currentVariable];
if (totalData !== null)
{
var tempData = newData();
var tempData2 = totalData;
dataArray[dataCount] = tempData;
var joinMark = countArray(dataCount);
totalData = google.visualization.data.join(tempData2,tempData,'full',[[0,0]],joinMark,[1]);
dataCount = dataCount+1;
}
else
{
totalData = newData();
dataArray[dataCount] = totalData;
dataCount = 1;
}
var options =
{
hAxis: {title: xTitle},
vAxes: {0: {title: y1Type},
1: {title: y2Type}
},
series: calculateSeries(),
pointSize: 0.5,
legend: {position: 'top', textStyle: {fontSize: 10}}
};
var chart = new google.visualization.ScatterChart(document.getElementById('graph'));
console.log(calculateSeries());
chart.draw(totalData, options);
function countArray(count)
{
var arrayCount= new Array();
if (count===1)
{
arrayCount[0] = count;
}
else
{
for (var i=0;i<count;i++)
{
var temp = i+1;
arrayCount[i] = temp;
}
}
return arrayCount;
}
function calculateSeries()
{
var series = {};
for (i=0;i<axisAssignment.length;i++)
{
series[i] = {targetAxisIndex: axisAssignment[i]};
}
return series;
}
function newData()
{
var dataType = dataIn[0];
dataIn.shift();
var axis = dataSelect(dataType);
axisAssignment.push(axis);
var data = new google.visualization.DataTable();
data.addColumn('date', 'Date');
data.addColumn('number', "Node: "+currentNode+": "+titles[currentVariable]);
var num = (dataIn.length);
data.addRows(num/2);
var i = 0;
var j = 0;
while (i<num)
{
var d = (dataIn[i]);
if (i%2===0)
{
d = new Date(d);
data.setCell(j,0,d);
i++;
}
else
{
data.setCell(j,1,parseFloat(d));
i++;
j++;
}
}
return data;
}
function dataSelect(type)
{
var axisNumber;
if (y1Type === null || y1Type === type)
{
y1Type = type;
axisNumber = 0;
}
else if (y2Type === null || y2Type === type)
{
y2Type = type;
axisNumber = 1;
}
else
{
alert("You already have 2 axes assigned.\n\nPlease clear the graph \nor select more objects of \ntype"+y1Type+" or \ntype "+y2Type+" to continue.");
axisNumber = null;
}
return axisNumber;
}
}
Ok, it seems that it's an issue with my choice of ScatterChart,
var options =
{
hAxis: {title: xTitle},
series: calculateSeries(),
vAxes: {0: {title: y1Type },
1: {title: y2Type}
},
pointSize: 0.5,
legend: {position: 'top', textStyle: {fontSize: 10}}
};
var chart = new google.visualization.LineChart(document.getElementById('graph'));
chart.draw(totalData, options);
I've changed it to LineChart and it's working fine, by keeping the pointSize option, the appearance is almost completely unchanged. Thanks for your help juvian.

How to generate snapshot of amCharts without visiting browser

I am using amCharts library on my website. On the side block I am displaying chart snapshots (thumbnails) and if clicked it goes to the chart itself. To display these snapshots they should be pre-generated but only way I can do it when I am on a chart page. Also I need to update these snapshots by schedule every day. I need a way (if possible) to generate these snapshots without visiting a webpage, ideally through command line.
At the moment I am saving charts as images on the server-side:
<script src="/static/amcharts/amcharts.js" type="text/javascript"></script>
<script src="/static/amcharts/serial.js" type="text/javascript"></script>
<script src="/static/amcharts/amstock.js" type="text/javascript"></script>
<script src="/static/amcharts/exporting/amexport.js" type="text/javascript"></script>
<script src="/static/amcharts/exporting/rgbcolor.js" type="text/javascript"></script>
<script src="/static/amcharts/exporting/canvg.js" type="text/javascript"></script>
<script src="/static/amcharts/exporting/filesaver.js" type="text/javascript"></script>
<script>
var cdata = {{ chartData|safe }};
AmCharts.ready(function () {
generateChartData();
createStockChart();
});
var chartData = [];
function generateChartData() {
for (var i = 0; i < cdata.length; i++)
{
chartData.push({
date: new Date(cdata[i]["date"]),
value: cdata[i]["value"],
volume: cdata[i]["volume"]
});
}
}
var chart;
function createStockChart() {
chart = new AmCharts.AmStockChart();
chart.exportConfig = {
menuItems: []
};
chart.addListener('rendered', function (event) {
chart.AmExport.output({format:"png", output: 'datastring'},
function(data) {
$.post("/charts/save_chart_snapshot/", {
imageData: encodeURIComponent(data)
});
});
});
chart.pathToImages = "/static/amcharts/images/";
var categoryAxesSettings = new AmCharts.CategoryAxesSettings();
categoryAxesSettings.minPeriod = "DD";
chart.categoryAxesSettings = categoryAxesSettings;
// DATASETS //////////////////////////////////////////
var dataSet = new AmCharts.DataSet();
dataSet.color = "#9cc11a";
dataSet.fieldMappings = [{
fromField: "value",
toField: "value"
}, {
fromField: "volume",
toField: "volume"
}];
dataSet.dataProvider = chartData;
dataSet.categoryField = "date";
// set data sets to the chart
chart.dataSets = [dataSet];
// PANELS ///////////////////////////////////////////
// first stock panel
var stockPanel1 = new AmCharts.StockPanel();
stockPanel1.showCategoryAxis = false;
stockPanel1.title = "Value";
stockPanel1.percentHeight = 70;
// graph of first stock panel
var graph1 = new AmCharts.StockGraph();
graph1.valueField = "value";
graph1.type = "smoothedLine";
graph1.lineThickness = 2;
graph1.bullet = "round";
graph1.bulletBorderColor = "#FFFFFF";
graph1.bulletBorderAlpha = 1;
graph1.bulletBorderThickness = 3;
stockPanel1.addStockGraph(graph1);
// create stock legend
var stockLegend1 = new AmCharts.StockLegend();
stockLegend1.valueTextRegular = " ";
stockLegend1.markerType = "none";
stockPanel1.stockLegend = stockLegend1;
// second stock panel
var stockPanel2 = new AmCharts.StockPanel();
stockPanel2.title = "Volume";
stockPanel2.percentHeight = 30;
var graph2 = new AmCharts.StockGraph();
graph2.valueField = "volume";
graph2.type = "column";
graph2.cornerRadiusTop = 2;
graph2.fillAlphas = 1;
stockPanel2.addStockGraph(graph2);
// create stock legend
var stockLegend2 = new AmCharts.StockLegend();
stockLegend2.valueTextRegular = " ";
stockLegend2.markerType = "none";
stockPanel2.stockLegend = stockLegend2;
// set panels to the chart
chart.panels = [stockPanel1, stockPanel2];
// OTHER SETTINGS ////////////////////////////////////
var scrollbarSettings = new AmCharts.ChartScrollbarSettings();
scrollbarSettings.graph = graph1;
scrollbarSettings.updateOnReleaseOnly = true;
scrollbarSettings.usePeriod = "WW"; // this will improve performance
scrollbarSettings.position = "top";
chart.chartScrollbarSettings = scrollbarSettings;
var cursorSettings = new AmCharts.ChartCursorSettings();
cursorSettings.valueBalloonsEnabled = true;
chart.chartCursorSettings = cursorSettings;
// PERIOD SELECTOR ///////////////////////////////////
var periodSelector = new AmCharts.PeriodSelector();
periodSelector.position = "top";
periodSelector.dateFormat = "YYYY-MM-DD";
periodSelector.inputFieldWidth = 150;
periodSelector.periods = [{
period: "DD",
count: 1,
label: "1 day"
}, {
period: "DD",
count: 2,
label: "2 days"
}, {
period: "DD",
count: 5,
label: "5 days"
}, {
period: "DD",
count: 12,
label: "12 days"
}, {
period: "MAX",
label: "MAX"
}];
chart.periodSelector = periodSelector;
var panelsSettings = new AmCharts.PanelsSettings();
panelsSettings.usePrefixes = true;
chart.panelsSettings = panelsSettings;
chart.write('chartdiv');
}
</script>
And this is how I save it on server-side:
def save_chart_snapshot(request):
if request.POST:
data = urllib.unquote(request.POST['imageData'])
imageData = data.split(';')[1].split(',')[1]
filename = "static/temp/charts/snapshots/" + request.POST['isin'] + ".png"
if not os.path.exists(filename):
try:
fh = open(filename, "wb")
fh.write(imageData.decode('base64'))
except IOError:
logger.error("Cannot find file or read data: " + filename)
else:
fh.close()
logger.debug("Chart snapshot is generated: " + filename)
return HttpResponse("done")
This is a bit late but I ran into a similar situation recently where I needed export report capabilities at the server-side and wanted to use amcharts.js. As I was exploring, I found that it is possible to run amcharts using puppeteer that provides a headless browser.
Below article explains it in detail:
https://www.amcharts.com/docs/v4/tutorials/automating-report-generation-using-puppeteer/
There's also a downloadable example here.
Hope this helps someone looking for similar functionality.
You can probably automate this process by visiting your charts page using a WebKit "browser" in a virtual framebuffer using a tool like uzbl-core or netserf or wkhtmltopdf or similar (all of those support JS).
Then just add the above task to a script in a cron job that's kicked-off every day.
Technically you're still visiting the page in a browser with this solution, but at least it can be reasonably automated from a headless server in a datacenter.

Yui Datatable with AutoComplete and Pagination

I try to understand how use server-side Pagination for Dynamic Data (I see example : http://developer.yahoo.com/yui/examples/datatable/dt_dynamicdata.html).
My problem is I can not customize my request, when I try the pagination, my request is :
qct-list.htmlsort=geneid&dir=asc&startIndex=50&result=25
The request should be in the following form :
qct-list.html?name=cd4&symbol=cd4&start=0&limit=25 or
qct-list.html?name=cd4&symbol=cd4&start=25&limit=50 ...
Here is a copy (part) of my code :
var myColumnDefs = [
{key:"geneid", label:"Gene", sortable:true},
{key:"name", label:"Name", sortable:true},
{key:"symbol", label:"Symbol", sortable:true},
{key:"lastupdated", label:"Last Updated", formatter:"date", sortable:true},
{key:"lastmodified", label:"Last Modified", formatter:"date", sortable:true}
];
var myDataSource = new YAHOO.util.DataSource("qct-list.html");
myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
myDataSource.connXhrMode = "queueRequests";
myDataSource.responseSchema = {
resultsList: "geneItemList",
fields: ["col","qv","limit","start","geneid","name","symbol","lastupdated","lastmodified"],
metaFields: {
totalRecords: "totalRecords" // Access to value in the server response
}
};
var qctPaginator = new YAHOO.widget.Paginator({
rowsPerPage: 25,
totalRecords : YAHOO.widget.Paginator.VALUE_UNLIMITED,
template: YAHOO.widget.Paginator.TEMPLATE_ROWS_PER_PAGE,
rowsPerPageOptions: [25,50,75,100]
});
var myRequestBuilder = function(ostate, oSelf) {
oState = oState || {pagination:null};
var name = Dom.get('dt_input_name').value;
var symbol = Dom.get('dt_input_symbol').value;
var start = (oState.pagination) ? oState.pagination.recordOffset : 0;
var limit = (oState.pagination) ? oState.pagination.rowsPerPage : 25;
return "?name=" + name + "&symbol"+ symbol + "&start=" + start + "&limit=" + limit;
}
var oConfigs = {
dynamicData: true, // Enables dynamic server-driven data
selectionMode: "single",
paginator: qctPaginator ,
generateRequest : myRequestBuilder,
paginationEventHandler : YAHOO.widget.DataTable.handleDataSourcePagination,
initialRequest: "?name=&symbol=&start=0&limit="
};
var myGeneListTable = new YAHOO.widget.DataTable("geneListTable", myColumnDefs, myDataSource, oConfigs);
myGeneListTable.handleDataReturnPayload = function(oRequest, oResponse, oPayload) {
oPayload.totalRecords = oResponse.meta.totalRecords;
return oPayload;
}
Can someone explain how to use the pagination with DataTable and how to personalize request ?
I almost managed to make it work my pagination.
My RequestBuilder now works fine, I get the request I need :
qct-list.html?name=interferon&symbol=&start=25&limit=25
Here the new code :
var myColumnDefs = [
{key:"geneid", label:"Gene", sortable:true},
{key:"name", label:"Name", sortable:true},
{key:"symbol", label:"Symbol", sortable:true},
{key:"lastupdated", label:"Last Updated", formatter:"date", sortable:true},
{key:"lastmodified", label:"Last Modified", formatter:"date", sortable:true}
];
var myDataSource = new YAHOO.util.DataSource("qct-list.html");
myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
myDataSource.connXhrMode = "queueRequests";
myDataSource.responseSchema = {
resultsList: "geneItemList",
fields: ["col","qv","limit","start","geneid","name","symbol","lastupdated","lastmodified"],
metaFields: {
totalRecords: "totalRecords" // Access to value in the server response
}
};
var qctPaginator = new YAHOO.widget.Paginator({
rowsPerPage: 25,
rowsPerPageOptions: [25,50,75,100]
});
var myRequestBuilder = function(oState,oSelf)
{
oState = oState || {pagination:null, sortedBy:null};
// var sort = (oState.sortedBy) ? oState.sortedBy.key : "geneid";
// var dir = (oState.sortedBy && oState.sortedBy.dir === YAHOO.widget.DataTable.CLASS_DESC) ? "desc" : "asc";
var start = (oState.pagination != null) ? oState.pagination.recordOffset : 0;
var limit = (oState.pagination != null) ? oState.pagination.rowsPerPage : 25;
var name = Dom.get('dt_input_name').value || "";
var symbol = Dom.get('dt_input_symbol').value || "";
return '?name='+ name + '&symbol=' + symbol + '&start='+ start + '&limit=' + limit;
}
var oConfigs = {
initialRequest: "?name=interferon&symbol=&start=&limit=",
dynamicData: true, // Enables dynamic server-driven data
selectionMode: "single",
paginator: qctPaginator,
generateRequest : myRequestBuilder
};
var myGeneListTable = new YAHOO.widget.DataTable("geneListTable", myColumnDefs, myDataSource, oConfigs);
myGeneListTable.handleDataReturnPayload = function(oRequest, oResponse, oPayload) {
if (oPayload == undefined) {
oPayload = {};
}
oPayload.totalRecords = oResponse.meta.totalRecords;
return oPayload;
}
I think you need to swap the declaraion of oConfigs and myRequestBuilder around so that
var myRequestBuilder = ...
var oConfigs = ...

Categories