mages / googleVis

Interface between R and the Google Chart Tools
https://mages.github.io/googleVis/
360 stars 155 forks source link

Offline and fully embedded charts #99

Open py9mrg opened 11 months ago

py9mrg commented 11 months ago

Hello there,

I have recently come across this package and have a query about the documentation/usage. In the documentation it states that an internet connection is required, but I can make Google Charts work without it so I think that your package can be updated with a similar method to what i did. (I know basically zero javascript so forgive me if this is all wrong).

I'm going to base my example on Google's documentation.

Take a simple Gantt chart as an example, I can take Google's code:

<html>
<head>
  <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
  <script type="text/javascript">
    google.charts.load('current', {'packages':['gantt']});
    google.charts.setOnLoadCallback(drawChart);

    function daysToMilliseconds(days) {
      return days * 24 * 60 * 60 * 1000;
    }

    function drawChart() {

      var data = new google.visualization.DataTable();
      data.addColumn('string', 'Task ID');
      data.addColumn('string', 'Task Name');
      data.addColumn('date', 'Start Date');
      data.addColumn('date', 'End Date');
      data.addColumn('number', 'Duration');
      data.addColumn('number', 'Percent Complete');
      data.addColumn('string', 'Dependencies');

      data.addRows([
        ['Research', 'Find sources',
         new Date(2015, 0, 1), new Date(2015, 0, 5), null,  100,  null],
        ['Write', 'Write paper',
         null, new Date(2015, 0, 9), daysToMilliseconds(3), 25, 'Research,Outline'],
        ['Cite', 'Create bibliography',
         null, new Date(2015, 0, 7), daysToMilliseconds(1), 20, 'Research'],
        ['Complete', 'Hand in paper',
         null, new Date(2015, 0, 10), daysToMilliseconds(1), 0, 'Cite,Write'],
        ['Outline', 'Outline paper',
         null, new Date(2015, 0, 6), daysToMilliseconds(1), 100, 'Research']
      ]);

      var options = {
        height: 275
      };

      var chart = new google.visualization.Gantt(document.getElementById('chart_div'));

      chart.draw(data, options);
    }
  </script>
</head>
<body>
  <div id="chart_div"></div>
</body>
</html>

and save this as a local html file then everything works fine. I notice on the third line that the file loads an external .js script hosted by Google. Presumably this is equivalent to the line:

<!-- jsChart -->  
<script type="text/javascript" src="https://www.google.com/jsapi?callback=displayChartGanttID126c907ea00"></script>

if I use print on one of the first Gantt example in the googleVis documentation as the src links seem to go to very similar javascript.

Coming back to the Google example, if I copy everything in https://www.gstatic.com/charts/loader.js to a local file loader.js and then change the src to that file, then everything works offline.

Moreover, if I copy the contents of the file into the <script type="text/javascript">put everything here</script>, then I don't even need a separate file and everything works offline from the html file on it's own. (This seems equivalent to the sort of thing Quarto and Rmarkdown do if you choose the appropriate yaml option such as embed-resources: true for Quarto).

However, if I try the same trick with the output of a googleVis chart (i.e. print the html to a .html file, copy everything in the src link to a local file, or within the html) then it doesn't work - even though the src links seem to lead to identical javascript. I'm not sure why as it seems to me that if it works with the example from Google then it ought to work with googleVis as well - I guess it's something to do with the different ways googleVis and Google's example use a function to draw the actual chart. But, again, my javascript is basically non-existent.

So, my question is: couldn't the googleVis package be updated to do the same? Perhaps by using a drawing function closer to Google's paradigm? (I'm assuming this doesn't break any licences of course).

Edits: clarity.

Edit 2: A solution of sorts....

Ok, so, hacking away I think I have found a solution of sorts that may help. Using the Google documentation, I worked out the following. Note, as per the docs, this may not work for all charts.

The html has to be adjusted as follows:

  1. Move the following line(s) to the top of the <body> section (immediately before <!-- jsHeader -->, technically it could be immediately after as long as it's before the next <script>)
<!-- jsChart -->  
<script type="text/javascript" src="https://www.google.com/jsapi?callback=displayChartGanttID[appropriate-identifier]"></script>
  1. Add the following lines immediately after the <script> mentioned above within <!-- jsHeader -->
    google.charts.load('current', {'packages':['gantt']});
    google.charts.setOnLoadCallback(drawChartGanttID[appropriate-identifier]);
  1. Finally, for offline use, either a. have a local loader.js file and update the relevant line to src = loader.js, or b. copy the contents of https://www.gstatic.com/charts/loader.js (or https://www.google.com/jsapi?callback=displayChartGanttID[appropriate-identifier]) within the <script> call in part (1) and delete the relevant src = part.

You will then have a completely offline version of the html file.

I guess this package can be updated to write the html according to that scheme instead and everything should work offline. Albeit, you'd still need an internet connection to generate the file, but not to display it (which is helpful when sharing presentations etc). Thinking about it, if the package included a file of the javascript in https://www.gstatic.com/charts/loader.js then even the plot generation can be totally offline.