Skip to content
Advertisement

custom chart using google sheets data

I’m new to this, so please bear with me 🙂

I have a google sheet with columns: ‘Name, A-Score,T-Score, R-Score, P-Score’

Scores are numerical values from 0 to 20. This data needs to be converted to a special, but very simple graph which i don’t think is available in the existing sheets charting options. I’ve tried to write some code (i.e assembled from many stack overflow answers) to construct the graph i need:

myLearningStyleDiagram

myCodePenDraft

Question:

  1. Have i missed something basic in the existing chart options that would let me graph this way?

    …if not, is this possible to do in sheets with google-scripts or extensions?

  2. How could i go about efficiently creating individual charts (export to pngs) for a few hundred rows; perhaps by somehow reusing the code i’ve got so far.

Thanks so much!

//javascript
//data from sheets
var Name = "santayan";
var Activist = 12;
var Theorist = 9;
var Reflector = 14;
var Pragmatist = 12;

//set up canvas
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");

//set up click target
var link = document.getElementById("link");

//reset coordinates and white bg
ctx.translate(200, 200);
ctx.beginPath();
ctx.rect(-200, -200, 400, 400);
ctx.fillStyle = "white";
ctx.fill();

//plot main graph
ctx.lineCap = "round";
ctx.lineWidth = 5;
ctx.strokeStyle = "#806";

ctx.beginPath();
ctx.moveTo(Theorist * 10, 0);
ctx.lineTo(0, Reflector * 10);
ctx.lineTo(Activist * -10, 0);
ctx.lineTo(0, Pragmatist * -10);
ctx.closePath();
ctx.stroke();

//plot axes and labels
ctx.strokeStyle = "#d3d3d3";
ctx.lineWidth = 1;
ctx.moveTo(-200, 0);
ctx.lineTo(200, 0);
ctx.moveTo(0, -200);
ctx.lineTo(0, 200);
ctx.stroke();

ctx.font = "1em Helvetica";
ctx.textAlign = "right";
ctx.fillStyle = "#806";
ctx.fillText(Name, 195, -185);
ctx.font = ".5em Helvetica";
ctx.fillText("LEARNING STYLE", 195, -170);
ctx.fillStyle = "#000";
ctx.textAlign = "center";
ctx.fillText("ACTIVIST", -170, 12);
ctx.fillText("THEORIST", 170, -5);
ctx.rotate(Math.PI / 2);
ctx.fillText("PRAGMATIST", -165, 12);
ctx.fillText("REFLECTOR", 170, -5);

//click handler
function downloadImage() {
  link.setAttribute("download", Name + " LearningStyle.png");
  link.setAttribute(
    "href",
    canvas.toDataURL("image/png").replace("image/png", "image/octet-stream")
  );
}

//html
<div class="container"> <a id="link" OnClick=downloadImage()><canvas id="myCanvas" width="400" height="400" </canvas></a> click on the image to download</div>    
//css
a{cursor:pointer}body{font-family:Helvetica,Sans;font-size:.8em;text-align:right;background-color:#888;margin:0}.container{width:440px;height:460px;display:block;background-color:#aaa;margin:0 auto;padding:10px}canvas{box-sizing:border-box;background:#fff;padding:20px;margin:0 auto;border:1px solid grey;border-box:inner}

Advertisement

Answer

There is no Google Chart for this type, but you can achieve your goal with the method you suggested.

Unfortunately there is no option for this type of chart in Google at the moment. Though your code looks like it draws it fine! To get this working with a sheet you would need to use the HtmlService to render client side HTML and JavaScript. Then to communicate between the client and server (Apps Script to interface with your sheet and drive), you would use google.script.run..., see Client Server Communications.

Is this the most efficient way? Probably not. Yet this is a reasonably concise way of doing it totally within Apps Script.

I would test this with 10 rows, then 20, then 50 etc. In case it gets overwhelmed with too many.

Working Sample

You need two files in your Apps Script project:

chart.html

<!DOCTYPE html>

<html>
<head>
<style>
  a {cursor:pointer;}body{font-family:Helvetica,Sans;font-size:.8em;text-align:right;background-color:#888;margin:0}.container{width:440px;height:460px;display:block;background-color:#aaa;margin:0 auto;padding:10px}canvas{box-sizing:border-box;background:#fff;padding:20px;margin:0 auto;border:1px solid grey;border-box:inner}
</style>
</head>

<body>
<div class="container">
    <canvas id="myCanvas" width="400" height="400"></canvas>
</div>
</body>

<script>
// Your drawing code wrapped in a function
function drawChart(Name, Activist, Theorist, Reflector, Pragmatist){
  var canvas = document.getElementById("myCanvas");
  var ctx = canvas.getContext("2d");

  //reset coordinates and white bg
  ctx.translate(200, 200);
  ctx.beginPath();
  ctx.rect(-200, -200, 400, 400);
  ctx.fillStyle = "white";
  ctx.fill();

  //plot main graph
  ctx.lineCap = "round";
  ctx.lineWidth = 5;
  ctx.strokeStyle = "#806";

  ctx.beginPath();
  ctx.moveTo(Theorist * 10, 0);
  ctx.lineTo(0, Reflector * 10);
  ctx.lineTo(Activist * -10, 0);
  ctx.lineTo(0, Pragmatist * -10);
  ctx.closePath();
  ctx.stroke();

  //plot axes and labels
  ctx.strokeStyle = "#d3d3d3";
  ctx.lineWidth = 1;
  ctx.moveTo(-200, 0);
  ctx.lineTo(200, 0);
  ctx.moveTo(0, -200);
  ctx.lineTo(0, 200);
  ctx.stroke();

  ctx.font = "1em Helvetica";
  ctx.textAlign = "right";
  ctx.fillStyle = "#806";
  ctx.fillText(Name, 195, -185);
  ctx.font = ".5em Helvetica";
  ctx.fillText("LEARNING STYLE", 195, -170);
  ctx.fillStyle = "#000";
  ctx.textAlign = "center";
  ctx.fillText("ACTIVIST", -170, 12);
  ctx.fillText("THEORIST", 170, -5);
  ctx.rotate(Math.PI / 2);
  ctx.fillText("PRAGMATIST", -165, 12);
  ctx.fillText("REFLECTOR", 170, -5);
  
  // Had to add this to reset the canvas properly
  ctx.resetTransform();
}

function getImage(Name) {
  var canvas = document.getElementById("myCanvas");
  var dataURL = canvas.toDataURL("image/png");
  return dataURL;
}

function notification(msg){
  let message = document.createElement("p");
  message.textContent = msg
  document.body.appendChild(message)
}

function main(data){
  // data is 2D array

  data.forEach(row => {
    drawChart(row[0], row[1], row[2], row[3], row[4]);
    let png = getImage();
    google.script.run.withSuccessHandler(notification).saveToDrive(row[0], png)
  })
}

google.script.run.withSuccessHandler(main).getData();

</script>

</html>

Code.gs

function getData() {
  let file = SpreadsheetApp.getActive();
  let sheet = file.getSheetByName("Sheet1");
  let range = sheet.getDataRange();
  let values = range.getValues();

  // Get rid of headers
  values.shift()
  return values;
}

function saveToDrive(name, dataURL){
  // https://stackoverflow.com/questions/56092146/convert-data-uri-using-google-apps-script-blob
  var type = (dataURL.split(";")[0]).replace('data:','');
  var imageUpload = Utilities.base64Decode(dataURL.split(",")[1]);
  var blob = Utilities.newBlob(imageUpload, type, name + ".png");

  let newFile = DriveApp.createFile(blob);
  newFile.setName(name)
  return name + "'s chart saved!"
}

function main(){
  let html = HtmlService.createHtmlOutputFromFile("chart.html");
  SpreadsheetApp.getUi().showSidebar(html);
}

Spreadsheet looks like this:

enter image description here

Order of operations for script

  1. Launch script by running main from Code.gs and authorize it.
  2. Create an HTML output, complete with the client side JS as shown above.
  3. Load that HTML in the sheet’s sidebar SpreadsheetApp.getUi().showSidebar(html);
  4. The client-side HTML then sends an asynchronous request to the main Apps Script (google.script.run) to get all the data from the spreadsheet, which it will receive as a two dimensional array.
  5. One the data makes it back, client-side JS will go through each row, draw the chart, save it as a dataURL string, and sends another asynchronous request to Apps Script to convert the dataURL into a png blob, saving it in Drive, and returning a message saying that it completed.

Notes

  • Tried to send it from the client as a blob already, but I was getting strange errors and so defaulted to using the dataURL.
  • Had to add in ctx.resetTransform(); and the end of the drawing function to reset the ctx properly.
  • The index numbers for the columns are hard-coded, so please be aware that this script will only work as-is if your spreadsheet is in exactly the same format.

Result

The side bar appears and a chart. For so few drawings, I only saw it go from white to the final chart. Afterwards, below the chart, the completion messages start to appear:

enter image description here

Then if you go to your main Drive folder, you should see the PNG files!

References

User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement