I have been able to show a google chart from mysql data, but when I add the second chart I am only able to see the data from the 2nd array(for 2nd chart) I used json_encode on in my php script. If I change the order of the array encoding so that the 2nd chart's array is now encoded first I no longer see it, but now the first chart is visible. Can anyone see the issue? Maybe I should use column charts instead of material charts??
here is my javascript:
<script type="text/javascript">
google.setOnLoadCallback(drawCharts);
function drawCharts() {
drawChartA();
drawChartB();
}
function drawChartB(){
var data = new google.visualization.DataTable(<?=$jsonTable?>);
var options = {
chart: {
title: 'Calls for <?php echo $cLabel;?>',
subtitle: 'Something to put Here',
},
annotations:{
textStyle:{
fontName: 'Times-Roman',
fontSize: 12,
bold: true,
italic: false
}
},
width: 1200,
height: 600,
};
var chart = new
google.charts.Bar(document.getElementById('chart_div'));
chart.draw(data, google.charts.Bar.convertOptions(options));
}
function drawChartA(){
var data = new google.visualization.DataTable(<?=$jsonTable_ct?>);
var options = {
chart: {
title: 'Calls for <?php echo $cLabel;?>',
subtitle: 'Something to put Here',
},
annotations:{
textStyle:{
fontName: 'Times-Roman',
fontSize: 12,
bold: true,
italic: false
}
},
width: 1200,
height: 600,
isStacked: 'true',
};
var chart = new google.charts.Bar(document.getElementById('chart_div_ct'));
chart.draw(data, google.charts.Bar.convertOptions(options));
}
</script>
my json_encoded files are:
{"cols":[{"label":"Time Interval","type":"string"},{"label":"Calls - All Offices","type":"number"}],"rows":[{"c":[{"v":"05:00"},{"v":1}]},{"c":[{"v":"06:00"},{"v":3}]},{"c":[{"v":"07:00"},{"v":9}]},{"c":[{"v":"07:30"},{"v":22}]},{"c":[{"v":"08:00"},{"v":82}]},{"c":[{"v":"08:30"},{"v":68}]},{"c":[{"v":"09:00"},{"v":97}]},{"c":[{"v":"09:30"},{"v":48}]},{"c":[{"v":"10:00"},{"v":56}]},{"c":[{"v":"10:30"},{"v":70}]},{"c":[{"v":"11:00"},{"v":75}]},{"c":[{"v":"11:30"},{"v":53}]},{"c":[{"v":"12:00"},{"v":56}]},{"c":[{"v":"12:30"},{"v":48}]},{"c":[{"v":"13:00"},{"v":22}]},{"c":[{"v":"13:30"},{"v":42}]},{"c":[{"v":"14:00"},{"v":40}]},{"c":[{"v":"14:30"},{"v":60}]},{"c":[{"v":"15:00"},{"v":69}]},{"c":[{"v":"15:30"},{"v":65}]},{"c":[{"v":"16:00"},{"v":73}]},{"c":[{"v":"16:30"},{"v":37}]},{"c":[{"v":"17:00"},{"v":20}]},{"c":[{"v":"17:30"},{"v":10}]},{"c":[{"v":"18:00"},{"v":10}]},{"c":[{"v":"18:30"},{"v":2}]},{"c":[{"v":"19:00"},{"v":1}]},{"c":[{"v":"19:30"},{"v":2}]},{"c":[{"v":"20:00"},{"v":1}]},{"c":[{"v":"20:30"},{"v":1}]}]}
and the other is here: (they both work so reviewing this may not be necessary)
{"cols":[{"label":"Time Interval","type":"string"},{"label":"NP Calls","type":"number"},{"label":"DR Calls","type":"number"},{"label":"RE Today Calls","type":"number"},{"label":"RE Future","type":"number"},{"label":"ACCT","type":"number"},{"label":"EMER","type":"number"},{"label":"Other Calls","type":"number"}],"rows":[{"c":[{"v":"05:00"},{"v":0},{"v":0},{"v":0},{"v":0},{"v":0},{"v":0},{"v":1}]},{"c":[{"v":"06:00"},{"v":0},{"v":0},{"v":1},{"v":0},{"v":0},{"v":0},{"v":0}]},{"c":[{"v":"07:00"},{"v":0},{"v":0},{"v":0},{"v":0},{"v":0},{"v":0},{"v":2}]},{"c":[{"v":"07:30"},{"v":2},{"v":0},{"v":3},{"v":0},{"v":0},{"v":1},{"v":2}]},{"c":[{"v":"08:00"},{"v":9},{"v":3},{"v":11},{"v":5},{"v":0},{"v":4},{"v":23}]},{"c":[{"v":"08:30"},{"v":1},{"v":2},{"v":13},{"v":7},{"v":2},{"v":4},{"v":14}]},{"c":[{"v":"09:00"},{"v":3},{"v":1},{"v":15},{"v":11},{"v":6},{"v":3},{"v":23}]},{"c":[{"v":"09:30"},{"v":0},{"v":0},{"v":4},{"v":6},{"v":5},{"v":0},{"v":16}]},{"c":[{"v":"10:00"},{"v":1},{"v":3},{"v":2},{"v":10},{"v":2},{"v":0},{"v":17}]},{"c":[{"v":"10:30"},{"v":5},{"v":1},{"v":1},{"v":10},{"v":2},{"v":3},{"v":23}]},{"c":[{"v":"11:00"},{"v":5},{"v":3},{"v":7},{"v":11},{"v":10},{"v":1},{"v":23}]},{"c":[{"v":"11:30"},{"v":4},{"v":1},{"v":2},{"v":6},{"v":2},{"v":0},{"v":18}]},{"c":[{"v":"12:00"},{"v":3},{"v":0},{"v":5},{"v":11},{"v":2},{"v":0},{"v":21}]},{"c":[{"v":"12:30"},{"v":5},{"v":1},{"v":4},{"v":4},{"v":4},{"v":1},{"v":5}]},{"c":[{"v":"13:00"},{"v":2},{"v":1},{"v":3},{"v":2},{"v":2},{"v":0},{"v":6}]},{"c":[{"v":"13:30"},{"v":2},{"v":0},{"v":1},{"v":3},{"v":1},{"v":0},{"v":15}]},{"c":[{"v":"14:00"},{"v":5},{"v":3},{"v":1},{"v":5},{"v":3},{"v":1},{"v":4}]},{"c":[{"v":"14:30"},{"v":3},{"v":1},{"v":5},{"v":6},{"v":6},{"v":0},{"v":19}]},{"c":[{"v":"15:00"},{"v":3},{"v":1},{"v":4},{"v":8},{"v":4},{"v":1},{"v":22}]},{"c":[{"v":"15:30"},{"v":8},{"v":1},{"v":0},{"v":10},{"v":4},{"v":0},{"v":22}]},{"c":[{"v":"16:00"},{"v":6},{"v":5},{"v":1},{"v":12},{"v":3},{"v":2},{"v":20}]},{"c":[{"v":"16:30"},{"v":3},{"v":4},{"v":3},{"v":7},{"v":3},{"v":1},{"v":7}]},{"c":[{"v":"17:00"},{"v":1},{"v":0},{"v":0},{"v":4},{"v":1},{"v":0},{"v":5}]},{"c":[{"v":"17:30"},{"v":0},{"v":1},{"v":0},{"v":1},{"v":1},{"v":0},{"v":1}]},{"c":[{"v":"18:00"},{"v":1},{"v":0},{"v":1},{"v":0},{"v":0},{"v":0},{"v":2}]},{"c":[{"v":"18:30"},{"v":0},{"v":0},{"v":0},{"v":1},{"v":0},{"v":0},{"v":0}]}]}Can
It's most likely the same issue that was reported in google-visualization-issues repository.
There are at least two solution available at the moment:
Option 1. Render charts synchronously
The general idea is to render chart synchronously. Since draw function is asyncronous, we utilize ready event handler for that purpose.
Place ready event handler in every function before draw function invocation:
if (typeof ready != "undefined") google.visualization.events.addOneTimeListener(chart, 'ready', ready);
and change drawChartN() function signature to drawChartN(ready)
Then replace:
function drawCharts() {
drawChartA();
drawChartB();
}
with:
function drawCharts() {
drawChartA(function(){
drawChartB();
});
}
PhpFiddle
Option 2. Using the frozen version loader.
Since
The rollout of the v43 candidate release that would fix this problem
switch to using the frozen version loader.
Steps:
1)Add a reference to loader: <script
src="//www.gstatic.com/charts/loader.js"></script>
2)Then load a 43 version of library: google.charts.load("43",{packages:["corechart","bar"]});
3)Replace:
function drawCharts() {
drawChartA();
drawChartB();
}
with
google.charts.setOnLoadCallback(drawChartA);
google.charts.setOnLoadCallback(drawChartB);
Firstly I appreciate my request is quite "ambitious", but any help is greatly appreciated as I'm not sure the best way to proceed.
On my site (built with PHP/MySQL) after a user has uploaded a PDF I would like to display the PDF inline on the page (I'm assuming in an iFrame). I then need them to be able to drag out a number of "boxes" on top of the PDF (I'm assuming with jQuery). I then need to record the co-ordinates of this box so then later I can re-create the PDF injecting new text into the defined "boxes".
Does this sound feasible? If not what else would you suggest? (please don't say imagemagick!)
I know how to recreate a PDF injecting new text, but my issue is with how to allow the user to record those coordinates.
You could use PDF.js to render the PDF on the page. PDF.js will display it as part of the page so you can attach events and interact with it in ways you could not if it was being displayed by the Acrobat plugin.
I couldn't find a preexisting library for getting the coordinates so I whipped up this code to implement it.
Live demo of selection code
$(function () {
"use strict";
var startX,
startY,
selectedBoxes = [],
$selectionMarquee = $('#selectionMarquee'),
positionBox = function ($box, coordinates) {
$box.css(
'top', coordinates.top
).css(
'left', coordinates.left
).css(
'height', coordinates.bottom - coordinates.top
).css(
'width', coordinates.right - coordinates.left
);
},
compareNumbers = function (a, b) {
return a - b;
},
getBoxCoordinates = function (startX, startY, endX, endY) {
var x = [startX, endX].sort(compareNumbers),
y = [startY, endY].sort(compareNumbers);
return {
top: y[0],
left: x[0],
right: x[1],
bottom: y[1]
};
},
trackMouse = function (event) {
var position = getBoxCoordinates(startX, startY,
event.pageX, event.pageY);
positionBox($selectionMarquee, position);
};
$(document).on('mousedown', function (event) {
startX = event.pageX;
startY = event.pageY;
positionBox($selectionMarquee,
getBoxCoordinates(startX, startY, startX, startY));
$selectionMarquee.show();
$(this).on('mousemove', trackMouse);
}).on('mouseup', function (event) {
var position,
$selectedBox;
$selectionMarquee.hide();
position = getBoxCoordinates(startX, startY,
event.pageX, event.pageY);
if (position.left !== position.right &&
position.top !== position.bottom) {
$selectedBox = $('<div class="selected-box"></div>');
$selectedBox.hide();
$('body').append($selectedBox);
positionBox($selectedBox, position);
$selectedBox.show();
selectedBoxes.push(position);
$(this).off('mousemove', trackMouse);
}
});
});
You will have to tweak it to get coordinates that are relative to the PDF once you display it, but this should get you on the right track.
When I attempt to load a Google Chart in node.js, nothing happens.
I tried loading the first example from the line chart docs in both zombie.js and jsdom, but the chart never loads in either case.
The end goal is to retrieve the SVG data of the generated chart for export into an image or PDF. So if an alternate method (server side using node.js or PHP) to achieve this is possible, I'm open to suggestions.
NOTE: I have successfully generated a images of a few charts using gChartPhp, but the requirements of this project state that the embedded version be the interactive version provided by the current API and the exported version be visually IDENTICAL to the embedded one (without being interactive, obviously).
Edit: I tagged PhantomJS, since that is the solution with which I ultimately went.
Sorry for the lack of links, but the spam prevention mechanism will only allow me to post 2.
I'm 8 years late but I've just released an open-source project Google Charts Node that renders chart images in Puppeteer (somewhat of a successor to the original PhantomJS solution).
google-charts-node is available as an NPM library and can be used like so:
const GoogleChartsNode = require('google-charts-node');
function drawChart() {
// Set up your chart here, just like in the browser
// ...
const chart = new google.visualization.BarChart(container);
chart.draw(data, options);
}
// Render the chart to image
const image = await GoogleChartsNode.render(drawChart, {
width: 400,
height: 300,
});
Now you can save this image buffer as a file or return it as an HTTP response, etc.
It was pretty straightforward to create this. The main caveats were:
Not all charts support getImageURI, so I fall back to puppeteer to take a screenshot when this happens.
It's slow! But if you must use Google Charts as a requirement, you don't really have an alternative. This problem can be mitigated with enough cloud compute resources.
You can view the full source at the Github project, but here's the raw puppeteer flow if you want to do it yourself:
async function render() {
// Puppeteer setup
const browser = await puppeteer.launch();
const page = await browser.newPage();
// Add the chart
await page.setContent(`...Insert your Google Charts code here...`);
// Use getImageURI if available (not all charts support)
const imageBase64 = await page.evaluate(() => {
if (!window.chart || typeof window.chart.getImageURI === 'undefined') {
return null;
}
return window.chart.getImageURI();
});
let buf;
if (imageBase64) {
buf = Buffer.from(imageBase64.slice('data:image/png;base64,'.length), 'base64');
} else {
// getImageURI was not available - take a screenshot using puppeteer
const elt = await page.$('#chart_div');
buf = await elt.screenshot();
}
await browser.close();
return buf;
}
It wasn't the ideal solution, but I found an alternative to node.js for accomplishing the same end goal in PhantomJS. Simply create an HTML file containing the chart (test.html) and like node.js, create a JS file containing your code (test.js). Then run your JS file with PhantomJS.
In your JS file, open your HTML file as a webpage, then render it, either saving the image buffer to a file:
var page = require('webpage').create();
page.open('test.html', function () {
page.render('test.png');
phantom.exit();
});
Then run it:
phantomjs test.js
To dynamically create a chart, create the following JS file (test2.js):
var system = require('system');
var page = require('webpage').create();
page.onCallback = function(data)
{
page.clipRect = data.clipRect;
page.render('test.png');
phantom.exit();
};
page.includeJs('http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js', function()
{
page.includeJs('https://www.google.com/jsapi', function()
{
page.evaluate(function(chartType, data_json, options_json)
{
var div = $('<div />').attr('id', 'chart').width(900).height(500).appendTo($('body'));
google.load("visualization", "1",
{
packages:[chartType == 'GeoChart' ? 'geochart' : 'corechart'],
callback: function()
{
data_arr = $.parseJSON(data_json);
data = google.visualization.arrayToDataTable(data_arr);
options = $.parseJSON(options_json);
chart = new google.visualization[chartType]($(div).get(0));
google.visualization.events.addListener(chart, 'ready', function()
{
window.callPhantom(
{
clipRect: $(div).get(0).getBoundingClientRect()
});
});
chart.draw(data, options);
}
});
}, system.args[1], system.args[2], system.args[3]);
});
});
Then run it:
phantomjs test2.js LineChart '[["Date","Steve","David","Other"],["Dec 31",8,5,3],["Jan 1",7,10,4],["Jan 2",9,4,3],["Jan 3",7,5,3]]' '{"hAxis.slantedText":true}'
phantomjs test2.js PieChart '[["Employee","Calls"],["Steve",31],["David",24],["Other",13]]' '{"is3D":true}'
phantomjs test2.js GeoChart '[["State","Calls"],["US-CA",7],["US-TX",5],["US-FL",4],["US-NY",8]]' '{"region":"US","resolution":"provinces"}'
To get the image data from an external script, make a copy of test2.js (test3.js) and change
page.render('test.png');
to
console.log(page.renderBase64('png'));
Then call it (from PHP, for example):
<?php
$data = array(
array("Employee", "Calls"),
array("Steve", 31),
array("David", 24),
array("Other", 13)
);
$options = array(
"is3D" => true
);
$command = "phantomjs test3.js PieChart '" . json_encode($data) . "' '" . json_encode($options) . "'";
unset($output);
$result = exec($command, $output);
$base64_image = implode("\n", $output);
$image = base64_decode($base64_image);
?>
NOTE: Looking back on this whole process, the problem I was having with node.js was possibly that I didn't setup callbacks or timeouts to wait until the charts were "ready".
So after hours of websearching, googling and overflowing i can't find the solution to my problem.
I got a linechart from Google charts. I want to convert it to PNG, save it on the server en insert it into a MySQL database.
Sounds simple, but i cant get it to work. The script from this website isnt working anymore (atleast not here) http://www.battlehorse.net/page/topics/charts/save_google_charts_as_image.html -> Not working.
Second option is the old option:
$imageData = file_get_contents('http://chart.apis.google.com/chart... etc');
I cant use that because its not supported anymore and cant get some decent quality out of it.
Is there anybody here that can give a good tutorial or help for my problem?
EDIT:
I used the code from Battlehorse combined with the code from EriC.
So now i got this working to show the chart as an image in a DIV i want to save this image on the server and update the mysql to use it in the future to use it in PDF files.
When you visit the site, paste this in the console (overwriting the malfunctioning function).
function getImgData(chartContainer) {
var chartArea = chartContainer.getElementsByTagName('svg')[0].parentNode;
var svg = chartArea.innerHTML;
var doc = chartContainer.ownerDocument;
var canvas = doc.createElement('canvas');
canvas.setAttribute('width', chartArea.offsetWidth);
canvas.setAttribute('height', chartArea.offsetHeight);
canvas.setAttribute(
'style',
'position: absolute; ' +
'top: ' + (-chartArea.offsetHeight * 2) + 'px;' +
'left: ' + (-chartArea.offsetWidth * 2) + 'px;');
doc.body.appendChild(canvas);
canvg(canvas, svg);
var imgData = canvas.toDataURL("image/png");
canvas.parentNode.removeChild(canvas);
return imgData;
}
In JS it was searching for an iframe bla bla to get the svg.
To automatically save the image, you can just let the method being invoked programmatically.
document.body.addEventListener("load", function() {
saveAsImg( document.getElementById("pie_div")); // or your ID
}, false );
For saving images serverside, this post could be helpful save a PNG image server-side
Update
Posting images to PHP (index.js)
function saveToPHP( imgdata ) {
var script = document.createElement("SCRIPT");
script.setAttribute( 'type', 'text/javascript' );
script.setAttribute( 'src', 'save.php?data=' + imgdata );
document.head.appendChild( script );
}
function save() {
var canvas = document.getElementById("canvas"), // Get your canvas
imgdata = canvas.toDataURL();
saveToPHP( imgdata );
}
function drawOnCanvas() {
var canvas = document.getElementById("canvas"), // Get your canvas
ctx = canvas.getContext("2d");
ctx.strokeStyle = "#000000";
ctx.fillStyle = "#FFFF00";
ctx.beginPath();
ctx.arc(100,99,50,0,Math.PI*2,true);
ctx.closePath();
ctx.stroke();
ctx.fill();
}
drawOnCanvas(); // Test
save();
save.php
<?php
// Get the request
$data = $_GET['data'];
// Save to your DB.
?>
You can use the grChartImg library. It's a cross browser solution and supports even old versions of IE (8 and earlier).It has many features such as download image,upload to the server, show the image in a dialog etc.
For more info look at http://www.chartstoimage.eu.
i hope help you.
This is not really an answer but might be one in the future and it is nescesary if you just want the feature back. The following URL shows all the current issues and feature requests for the visualization API.
https://code.google.com/p/google-visualization-api-issues/issues/list?can=2&q=&sort=-stars+id&colspec=ID%20Type%20Status%20Priority%20Milestone%20Owner%20Summary%20Stars
The more stars/votes this feature request gets, the higher the chance they will take a look at it.
I have the same issue - Save Google charts as image on server. None of answers here works for me. Finally I get solution but with some bugs(working only in Chrome browser). As base I used script from here https://gist.github.com/mpetherb/7085315 I made some changes for my project. I use jquery for importing generated graph image to to my server.
This is a graph that I want to convert to image and save google graph example id="ex0"
Script for converting to image and importing to server
<script>
function getImgData(chartContainer) {
var chartArea = chartContainer.getElementsByTagName('svg')[0].parentNode;
var svg = chartArea.innerHTML;
var doc = chartContainer.ownerDocument;
var canvas = doc.createElement('canvas');
canvas.setAttribute('width', chartArea.offsetWidth);
canvas.setAttribute('height', chartArea.offsetHeight);
canvas.setAttribute(
'style',
'position: absolute; ' +
'top: ' + (-chartArea.offsetHeight * 2) + 'px;' +
'left: ' + (-chartArea.offsetWidth * 2) + 'px;');
doc.body.appendChild(canvas);
canvg(canvas, svg);
var imgData = canvas.toDataURL("image/png");
canvas.parentNode.removeChild(canvas);
return imgData;
}
function toImg(chartContainer, imgContainer) {
var doc = chartContainer.ownerDocument;
var img = doc.createElement('img');
var myimg=img.src = getImgData(chartContainer);
//Here I am using jquery for importing decoded image to hidden.php on my server
$.ajax({
method: "POST",
url: "hidden.php",
data: { name: myimg } });
while (imgContainer.firstChild) {
imgContainer.removeChild(imgContainer.firstChild);
}
imgContainer.appendChild(img);
}
</script>
<button onclick="toImg(document.getElementById('ex0'), document.getElementById('ex0'));"
type="button" <Convert to image and upload on server></button>
// ex0 - div id of this type of google graph. If you using another type of google graph - you should change it
Don't forget include jquery to your code.
and php hidden script for receiving data from jquery method POST and saving it on server
hidden.php file
<?php
if(isset($_POST['name']))
{
$data = $_POST['name'];
list($type, $data) = explode(';', $data);
list(, $data) = explode(',', $data);
file_put_contents('graph_temp.png', base64_decode($data));
I will notice again - works only in Chrome browser. Firefox also create image file on server but without any content (looks like firefox not support base64 encoded data)