Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We donβt allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 6 years ago.
Improve this question
The Google Chart Images API is currently deprecated and scheduled for retirement on 20th April 2015.
Is there any other free service that can replace it and allow generating chart images just by providing parameters in the URL?
Here's an example for a URL used to generate a PNG image, it can be used as an HTML img source and is useful especially in e-mails:
http://chart.apis.google.com/chart?chxl=1:|Apr+04|Apr+05|Apr+06|Apr+07|Apr+08|Apr+09&chxp=1,0,20,40,60,80,100&chxr=0,0,45&chxs=1,676767,11.5,0,lt,676767&chxt=y,x&chs=550x198&cht=ls&chco=3366CC,FF9900&chds=0,45,0,45&chd=t:7,12,11,9,13,7|11,26,45,24,22,27&chdl=Visits++++|Page+Views&chdlp=t&&chdls=333333,16&chg=100,20,0,0&chls=4|2
This will produce the following image that can be added easily via an <img> tag and is supported across all browser and email clients.
Following this announcement, we made a drop-in-replacement for Google Image Charts π and added gif animation on top of it π(chart animations in emails are awesome!!).
It's called Image-charts. No more server-side chart rendering pain, no scaling issues, it's blazing fast, 1 URL = 1 image chart.
... and QR Code as well:
https://image-charts.com/chart?
&chs=150x150
&cht=qr
&chl=Hello world
&choe=UTF-8
http://www.jfree.org/eastwood/ is an open-source implementation of the google chart api. Its not 100% faithful but was close enough for me.
At the moment I havent found a solution to actually "link directly" to a chart (see later). But it is possible to convert the charts to images / PNG, and that is the prerequisity. And by converting on the fly you can let users save the chart as an image to a file.
The modern google charts is build up in a <svg> tag. The content of this tag can be drawn on a <canvas> using the excellent canvg library.
When that is done, you can transfer the content of the canvas to an <img> by canvas.toDataURL. Here is an example :
First, include the canvg library
<script type="text/javascript" src="http://canvg.googlecode.com/svn/trunk/rgbcolor.js"></script>
<script type="text/javascript" src="http://canvg.googlecode.com/svn/trunk/StackBlur.js"></script>
<script type="text/javascript" src="http://canvg.googlecode.com/svn/trunk/canvg.js"></script>
Some markup - 3 tags, a <div> for the chart, a <canvas> and a <img>
<div id="chart_div" style="width: 400px; height: 300px"></div>
<canvas id="canvas"></canvas>
<img id="img" width="200">
Notice "width=200" for the image, just to demonstrate this actually is working :)
Draw a chart as "normal" (as anyone are used to do it), here minimalistic just for the test
function drawLineGraph() {
var data = new google.visualization.DataTable(someJSON);
chart = new google.visualization.BarChart(document.getElementById("chart_div"));
var options = {};
chart.draw(data, options);
}
Draw the chart on window.load. I use a setTimeout for effect, but in a real life scenario I think it would be best to use a google.visualization.events.addListener(xxx, 'ready', function.
window.onload = function() {
drawLineGraph();
setTimeout(function() {
//get SVG content
var chart = document.getElementById('chart_div').getElementsByTagName('svg')[0].parentNode;
var svg = chart.innerHTML;
//get the canvas, adjust width / height
var canvas = document.getElementById('canvas');
canvas.setAttribute('width', chart.offsetWidth);
canvas.setAttribute('height', chart.offsetHeight);
//transfer SVG to canvas by canvg
canvg(canvas, svg);
//get an image source by canvas.toDataURL and add to the image
var imgsrc = canvas.toDataURL("image/png");
document.getElementById('img').setAttribute('src', imgsrc);
}, 1000);
}
The src of the image will look something like :
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZAAAAEsCAYAAADtt+XCAAAdCklEQVR4Xu2dfcyWZfnHDyFwtZVz68WcJWO9UCRuD4T9KjdrJC0HI8wWbmiTGMJPiXBg8bL+IIhioxio4aaiZIPZCxi9qCtro/ZsCatZGzSiLdZcNtwSwugX8Ou8Nhji88B5f0+vPdf5fD
..
..
Of course going on and on....As normal. I have not yet tried to manipulate / remote link / sent this - only used it as image - but I am sure that this is quite easy!
Result / output for the code above :
My team at Ramen recently built exactly this. It's called ChartURL. It's not forever-free like Google Charts API is, but there is a pretty generous free tier.
It lets you construct URLs in two ways. First, you can encrypt the data into the URL. We use encryption for accounting purposes (since it's not forever-free). In both cases, you encode a template_slug and your data into the URL. The template_slug is a string representation of a chart configuration you can modify, preview, and save inside your account on ChartURL.com. So you can have email-bar-chart-1 and email-bar-chart-2 and timeseries-signups each with their own style/config, and then just send in the data you want graphed inside that template.
Here's an example of generating a URL in ruby:
# This is a working example. View fully commented version here:
# https://gist.github.com/ryana/055414a4804806263b82
require 'json'
require 'openssl'
require 'base64'
require 'cgi'
ENCRYPT_KEY = "dek-d7a46236eda961a6c3c18ffcc6b077ba87d27e9ae85f7842c6d427c265dd5f69d5131308d93332353d4a55a4b1160fcf516515a4a9f0aa50fbf2d7a2e7d0f1c5"
ENCRYPT_KEY_DIGEST = KEY_DIGEST = OpenSSL::Digest::SHA256.new(ENCRYPT_KEY).digest
PROJECT_TOKEN = "dt-RwYN"
def charturl_url(template_slug, data)
json = {options: data}.to_json
cipher = OpenSSL::Cipher.new 'AES-256-CBC'
cipher.encrypt
iv = cipher.random_iv
cipher.key = ENCRYPT_KEY_DIGEST
encrypted_json = cipher.update(json) + cipher.final
iv_for_url = CGI.escape(Base64.encode64(iv))
data_for_url = CGI.escape(Base64.encode64(encrypted_json))
"https://charturl.com/i/#{PROJECT_TOKEN}/#{template_slug}/#{iv_for_url}/#{data_for_url}"
end
# Call our helper
url = charturl_url("weekly-activity",
data: {columns: [["This Week", 10,12,41,9,14,15,15], ["Last Week", 9,14,21,21,20,3,5]]})
#=> https://charturl.com/i/dt-RwYN/weekly-activity/nEPfObOZ3zTivXZqri8ZLA%3D%3D%0A/7X6WrBHEdRcnutV0fU0sN8s9mHFGkkRr%2FZYJwb43p8PDzAJvGWd37zi6ro70%0AVJd9f%2FkSIq2dmJzGe%2BW6CSlpUIrhXHXogvXd%2B%2Fk4VOS%2BTSlnMBwKOSJJNpGZ%0AVrLZd%2Fgq1mSbiXQnc%2FydiTVcGL2DaA%3D%3D%0A
Because URLs have a character limit, we also provide an API that allows you to POST us data and we'll return a short URL:
# This is a working example. View fully commented version here:
# https://gist.github.com/ryana/d37fccd7af3c6c409164/
require 'json'
require 'typhoeus'
API_KEY = "dak-55045dac-bb35-40ac-80c8-874ab71c6f83"
def charturl_url(template_slug, options)
url = "https://charturl.com/short-urls.json?api_key=#{API_KEY}"
headers = {'Content-Type' => 'application/json'}
body = {template: template_slug, options: options}.to_json
surl_response = Typhoeus::Request.post(url, body: body, headers: headers)
raise("Error creating ShortURL: #{surl_response.inspect}") if !surl_response.success?
JSON.parse(surl_response.body)['short_url']
end
# Call our helper
url = charturl_url("weekly-activity", data: {columns: [["This week", 4,1,5,6,1,7,8], ["Last week", 1,5,3,1,6,2,6]]})
url #=> "https://charturl.com/r/J9lA"
I have also been searching for other similar services that could generate static image charts, but have been unsuccessful so far.
However an option is to create your own "service" using php scripts on your own server, to which you can pass parameters.
You could use a php charting library, for example something like pChart to generate the graphs with php, and return the .png image from the script.
Some problems with the javascript-based charting solutions are that you normally cannot use them if you also want to generate PDF's with charts on the fly, or if you want to generate the charts inside Rich-text Editors, or just use them in emails (as you already mentioned).
I was also encountered with such a problem where I needed static image based charts on server side using PHP. I found a way to achieve this using PhantomJS and Google Chart's javascript api. Following is an example of how to do this...
var webPage = require('webpage');
var page = webPage.create();
page.includeJs("https://www.gstatic.com/charts/loader.js", function() {
var expectedContent = '<html>' +
'<head>' +
'<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script> <script type="text/javascript">' +
'google.charts.load("current", {packages:["corechart"]});'+
'google.charts.setOnLoadCallback(drawChart);'+
'function drawChart() {'+
'var data = google.visualization.arrayToDataTable(['+
'[\'Task\', \'Hours per Day\'],'+
'[\'Work\', 11],'+
'[\'Eat\', 2],'+
'[\'Commute\', 2],'+
'[\'Watch TV\', 2],'+
'[\'Sleep\', 7]'+
']);'+
'var options = {'+
'title: \'My Daily Activities\','+
'is3D: true,'+
'};'+
'var chart = new google.visualization.PieChart(document.getElementById(\'piechart_3d\'));'+
'chart.draw(data, options);'+
'}'+
'</script>'+
'</head>'+
'<body>'+
'<div id="piechart_3d" style="width: 900px; height: 500px;"></div>'+
'</body>'+
'</html>';
var expectedLocation = 'http://www.phantomjs.org/';
page.setContent(expectedContent, expectedLocation);
window.setTimeout(function () {
page.render("mypng.png");
phantom.exit();
}, 1000); // Change timeout as required to allow sufficient time
});
After that you need to run this javascript file within your PHP:-
exec(ROOT_DIRECTORY . "\phantomjs.exe processImageData.js ", $output);
Related
I have been trying to translate text from HTML code. Here is an example:
var s = '<span>X stopped the</span><icon></icon><subject>breakout session</subject>'
When I try =GOOGLETRANSLATE(s,"en","fi") in Google Sheet, it also changes the tags formatting and translates tags into simple text. Whereas the translation should be only for X stopped the breakout session. But that is not the case.
Then I tried this function:
function TransLang(string){
return LanguageApp.translate(string,'en', 'fi', {contentType: 'text'});
}
This function worked well (for some time), but after that I got an error
Service invoked too many times in one day.
So I am stuck here. Is there any way that we can translate simple text of html code without translating/messing with HTML tags? Is there any regex that can avoid tags and translate all the other simple text?
I hope I am able to state my problem clearly. Please guide me if you have any suggestions. Thank you
Is the text you want always inside a single <span>? Or could there be more than one span or other element types?
This works for extracting the inner text from a single <span>:
function getSpanText() {
let s = '<span>X stopped the</span><icon></icon><subject>breakout session</subject>';
var text = s.match("(?<=<span>).+(?=<\/span>)")[0]
Logger.log(text);
return text
}
So, after a lot of digging, I have been able to find what I was looking for.
function Translator(S){
var sourceLang = "en";
var targetLang = "fi";
var url =
'https://translate.googleapis.com/translate_a/single?client=gtx&sl='
+
sourceLang +
'&tl=' +
targetLang +
'&dt=t&q=' +
encodeURI(S);
var result = JSON.parse(UrlFetchApp.fetch(url).getContentText());
return result[0][0][0];
}
This simple function calls Google translate Api and extracts the result from there. The best thing is you do not have to worry about the tags, as they are not translated by Google, so just the simple text is translated. There is just one limitation in the solution that Api calls are limited, so you can not make more than 5000 calls/day.
Why not using LanguageApp.translate as a custom JS-Function (Extensions >> AppScripts)?!
var spanish = LanguageApp.translate('This is a <strong>test</strong>',
'en', 'es', {contentType: 'html'});
// The code will generate "Esta es una <strong>prueba</strong>".
LanguageApp.translate (apidoc) accepts as fourth option a contentType, which can be text or html.
For huge tables be aware that there are daily limits (quotas)!
I am currently using this jQuery plugin on my site to pull in a single user review from my Google Places account:
https://github.com/peledies/google-places (unmodified).
It works ok - however I need to extend the code to somehow also pull in and display the avatar/image of the reviewer that I'm pulling in.
<script>
jQuery(document).ready(function() {
jQuery("#google-reviews").googlePlaces({
placeId: 'ChIJ_____....766FU3cfuE',
render: ['reviews'],
min_rating: 2,
max_rows: 0,
personsName: 'Jt D.'
});
});
</script>
I need to extend this to also include the avatar/image of the single review I am displaying.
I have found "profilePhotoUrl" is in the "reviewer" object in the JSON (https://developers.google.com/my-business/reference/rest/v4/accounts.locations.reviews) but I can't work out how to add that to the existing code.
After a bit of twiddling around, I found the following solution.
in the google-places.js, find the renderReviews function:
var renderReviews = function(reviews)
add this:
var img = reviews[i].profile_photo_url;
This returns the image stored in the review as a URL. You can then add it to the output within an img html tag:
if (name === plugin.settings.personsName) { html = html+" <img src="+ img +"> <div class='review-item'><div class='review-meta'><span class='review-author'>"+name+" | <span class='review-date'>"+date+"</span></div>"+stars+"<p class='review-text'>"+reviews[i].text+"</p></div>"
}
A bit of a newbie question for xml/krpano,
I have a list of json items that I want to be dynamically loaded into XML <hotspots>. I can loop through each item in JavaScript but I have no clue how to do the same loop in XML!
Check out this picture:
Imagine that each rectangle with an image is one item in a JSON list. Each rectangle you see is a <hotspot>. Right now these three hotspots are hardcoded into the XML file, but I want to dynamically load hotspots based on how many JSON list items exist.
Here is one hotspot. If my json list has 16 items, I would expect 16 hotspots
to be loaded.
<!--* video image thumbnail *-->
<hotspot name="start" distorted="true"
url="/panorama/%$panoId%/thumb.png"
ath="0" atv="0"
ox="0" oy="36"
vr_timeout="2000"
zorder="99"
scale="0.8"
onclick="changepano( loadscene(video_scene, null, MERGE|KEEPVIEW|KEEPMOVING, BLEND(1)); );"
alpha="0.0"
onloaded="if(vr_start_done === true, removehotspot(start); start_vr(); , tween(alpha,1); );"
/>
Your question is about dynamically generating hotspots in KRPano from a JSON list.
It is not really clear to me the way you wrote your question if you want to read the JSON from KRPano XML file (let's say FROM KRPano) or if you are expecting to use Javascript to ask KRPano to produce the hotspots.
These are two completly distinct ways of doing it :)
Because I'm lazy and I suppose you want to deal with JSON in JS, I go for this solution...
Loading a JSON file from Javascript
Your KRPano project should look like a core HTML file presenting Javascript to embed the KRPano plugin.
There, you can declare a script content in your HTML in which you will parse your JSON content and you ask KRPano to generate a hotspot. This method should be called when you are sure KRPano is ready, or get it called from KRPano when it is ready, using "onready" attribute.
myHotspotList.json content:
var myHotspotList = [
{
name: "myFirstHotspot",
atv: 15.0,
ath: 56.5686,
url: "myHotspotImage.jpg"
}
];
tour.html content:
<html>
...
<script url="myHotspotList.json'></script>
<script>
function generateHotspots() {
// First, we get the KRPano plugin
var myKRPano = document.getElementById('krpanoSWFObject');
// Now we parse the JSON object
for(var idx in myHotspotList) {
// Get the current Hotspot data
var currHotspot = myHotspotList[idx];
// Ask KRPano to create a hotspot with our current name
myKRPano.call("addhotspot('"+ currHotspot.name +"');");
// Now set various attributes to this hotspot
myKRPano.call("set(hotpost['"+ currHotspot.name +"'].atv, "+currHotspot.atv+");");
myKRPano.call("set(hotpost['"+ currHotspot.name +"'].ath, "+currHotspot.ath+");");
myKRPano.call("set(hotpost['"+ currHotspot.name +"'].url, '"+currHotspot.url+"');");
}
}
</script>
...
// When you ask for pano creation, give your generation method as callback
embedpano({target:"krpanoDIV", onready:generateHotspots});
...
</html>
I hope this help and you got the trick with calling JSON object attributes and all.
Regards
I was trying to develop a Chrome extension that can display me the last 3 news from a soccer news site (obviously the page is not open in any tab), by refreshing every 5 minutes. My ideea was to load the page inside an iframe and, once the page is loaded, access the page DOM and extract only the text nodes with the news. I've tried in many ways using ready and load functions, I tried to follow this solutions here but i always get warnings. My question is: is there a way I can do that without having troubles with cross-domain security? Are there any simple examples i can use?
Here's how you could do it using JQuery (please keep in mind I dont know JQuery, just saw this approach somewhere and thought it might work for you).
I put this in a popup and it worked....
<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js"></script>
<script>
function renderNews(newsList){
$('#news').html('');
$(newsList).each(function(i,item){
var link = document.createElement('a');
$(link).attr('href',item.link);
$(link).html(item.description);
$(link).click(function(){
chrome.tabs.create({url:$(this).attr('href')});
});
var linksDate = document.createElement('span');
//$(linksDate).text(item.date);
$(linksDate).text(item.day + '-' + item.month + ' ' + item.hour + ':' + item.minute+' - ');
var listItem = document.createElement('li');
$(listItem).append(linksDate).append(link);
$("#news").append(listItem);
});
}
function getNews() {
$.get("http://www.milannews.it/?action=search§ion=32", null, function(data, textStatus)
{
if(data) {
var news=$(data).find(".list").find('li').slice(0,3) ;
$("#status").text('');
var newsList=[];
$(news).each(function(i, item){
var newsItem={};
newsItem.description=$(item).find('a').html();
newsItem.link='http://www.milannews.it/'+$(item).find('a').attr('href');
newsItem.date=$(item).find('span').first().text();
newsItem.day=newsItem.date.split(' ')[0].split('.')[0];
newsItem.month=newsItem.date.split(' ')[0].split('.')[1];
newsItem.hour=newsItem.date.split(' ')[1].split(':')[0];
newsItem.minute=newsItem.date.split(' ')[1].split(':')[1];
newsList[i]=newsItem;
});
renderNews(newsList);
localStorage.setItem('oldNews',JSON.stringify(newsList));
}
});
}
function onPageLoad(){
if (localStorage["oldNews"]!=null) renderNews(JSON.parse(localStorage["oldNews"]));
getNews();
}
</script>
</head>
<body onload="onPageLoad();" style="width: 700px">
<ul id="news"></ul>
<div id="status">Checking for new news...</div>
</body>
</html>
And dont forget to put the urls your getting with the xhr stuff in the permissions part of your manifest....
http://code.google.com/chrome/extensions/xhr.html
Use xhr to load the page and use jQuery or a regex to parse the raw HTML for the data you are looking for.
Keep in mind that the destination site may not want to you access their site in such an automated fashion. Be respectful of their site and resources.
im a little stuck using the canvas element in html5, have scoured the net looking for a workable solution but to no avail!
the main issue is that I want to click a button and send just the canvas element on the page to the printer.
i have tried using toDataUrl() - but this just seems to be resulting in a blank white image which has none of the canvas content!
the other issue i am experiencing is that attempting to initiate any javascript functions using "onClick" attached to a form button seems to be being tempermental at best!
here is my current attempt - this works in the sense that it does open a new window and attempt to send it to printer and it does create a base64 string (tested this using the "dataUrl" output on the second lowest document.write line) but as previously mentioned the image appears to be completely blank! (the canvas itself is definitely filled, both with an imported image and some text)
function printCanv()
{
var dataUrl = document.getElementById('copy').toDataURL(); //attempt to save base64 string to server using this var
document.getElementById('testBox').value = dataUrl; //load data into textarea
//attempt open window and add canvas etc
win = window.open();
self.focus();
win.document.open();
win.document.write('<'+'html'+'><'+'head'+'><'+'style'+'>');
win.document.write('body, td { font-family: Verdana; font-size: 10pt;}');
win.document.write('<'+'/'+'style'+'><'+'/'+'head'+'><'+'body'+'>');
((image code is here but site will not let me post it for some reason?))
win.document.write(dataUrl);
win.document.write('<'+'/'+'body'+'><'+'/'+'html'+'>');
win.document.close();
win.print();
win.close();
}
note: the code from "win = window.open()" onwards is currently taken from a tutorial and not my own work!
it is currently being called using <body onload="printCanv";"> - for some reason I could not get this to work at all using a button (little chunk of code below is my attempt which seemed to fail)
<input type="button" id="test" value="click me" onClick="printCanv();"> </input>
apologies is this help request is all over the place! i haven't posted to a site like this before and it didn't like me posting some html/script!
thanks in advance for any help you may be able to offer :)
Edit: draw function:
function copydraw() { //function called "copydraw" (could be anything)
var testimage3 = document.getElementById('copy').getContext('2d'); //declare variable for canvas overall (inc paths)
var img3 = new Image(); //declare image variable called "img3"
var testVar = document.getElementById('userIn').value; //take value from userin box. only updating on browser refresh?
img3.onload = function(){ //when image has loaded (img.onload) run this function
testimage3.drawImage(img3,0,0); //draw "img" to testimage
testimage3.font = "30pt 'Arial'"; //font method varies font attrib (weight, size, face)
testimage3.fillStyle = "#000000"; //sets fill color
testimage3.fillText(testVar, 310, 360); //filltext method draws text (xup ydown)
}
img3.src = 'images/liecakeA4.jpg'; //source of image
}
This function works perfectly, it draws the object and adds text from the variable, but for some reason it seems to be preventing me from outputting it as an image. I'm really confused!
I'm not sure exactly what's wrong with your code, I suspect in your current version calling printCanv in the body load event will mean you're trying to print the canvas before it's drawn. Running it off the button should work better, I'm not sure why that wasn't working for you as there's no reason in principle why it shouldn't work.
To arrive at a working version I've modified your code slightly:
function printCanvas(el) {
var dataUrl = document.getElementById(el).toDataURL(); //attempt to save base64 string to server using this var
var windowContent = '<!DOCTYPE html>';
windowContent += '<html>'
windowContent += '<head><title>Print canvas</title></head>';
windowContent += '<body>'
windowContent += '<img src="' + dataUrl + '">';
windowContent += '</body>';
windowContent += '</html>';
var printWin = window.open('','','width=340,height=260');
printWin.document.open();
printWin.document.write(windowContent);
printWin.document.close();
printWin.focus();
printWin.print();
printWin.close();
}
This works for me in the Firefox 4.0 nightly.
One addition to the accepted-best-answer: It doesnt work here with Chrome 17.0.942.0 winvista, because the print-preview-dlg is shown within the website itself and printWin.close() will close the dlg too.
So printWin.close() must be delayed or initiated by the user, but those are no good solutions.
It would be best, if chrome printdlg could have a callback, sth. one knows, printing is done, and can close the window. If this is possible is another question.