Created
April 24, 2025 21:46
-
-
Save bpgould/63d8f849b5fa16df93ea4b9527a781d7 to your computer and use it in GitHub Desktop.
Generate Sankey Visualization in Google Sheets via AppScript
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// In Google Sheets > Extensions > App Script | |
// Copy in code and agree to permissions | |
// Run script - it will run on current open worksheet | |
// Worksheet must have 3 columns: From, To, Weight | |
// Script will generate the diagram with Google Charts, copy to image, close modal, and upload image to sheet | |
// The Script also adds a shortcut that allows you to run it from the Sheets Toolbar via Chart Tools | |
// One limitation is no tooltips in image (vs modal) and no easy way to label nodes with the weight value | |
function drawSankey() { | |
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet(); | |
const data = sheet.getRange("A1:C" + sheet.getLastRow()).getValues(); | |
const html = ` | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<base target="_top"> | |
<script src="https://www.gstatic.com/charts/loader.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script> | |
<script> | |
google.charts.load('current', { packages: ['sankey'] }); | |
google.charts.setOnLoadCallback(drawChart); | |
function drawChart() { | |
console.log("Drawing chart..."); | |
const data = new google.visualization.DataTable(); | |
data.addColumn('string', 'From'); | |
data.addColumn('string', 'To'); | |
data.addColumn('number', 'Weight'); | |
data.addRows(${JSON.stringify(data.slice(1))}); | |
const chartDiv = document.getElementById('chart'); | |
const chart = new google.visualization.Sankey(chartDiv); | |
google.visualization.events.addListener(chart, 'ready', () => { | |
console.log("Chart ready. Capturing image..."); | |
html2canvas(chartDiv).then(canvas => { | |
const scale = 0.5; // Adjust to stay under limits | |
const scaledCanvas = document.createElement("canvas"); | |
scaledCanvas.width = canvas.width * scale; | |
scaledCanvas.height = canvas.height * scale; | |
const ctx = scaledCanvas.getContext("2d"); | |
ctx.drawImage(canvas, 0, 0, scaledCanvas.width, scaledCanvas.height); | |
const imageData = scaledCanvas.toDataURL("image/png").replace(/^data:image\\/png;base64,/, ""); | |
google.script.run | |
.withSuccessHandler(() => { | |
console.log("Image inserted into sheet."); | |
google.script.host.close(); | |
}) | |
.withFailureHandler(err => { | |
console.error("Image upload failed:", err); | |
alert("Image upload failed: " + err.message); | |
google.script.host.close(); | |
}) | |
.insertImageToSheet(imageData); | |
}).catch(err => { | |
console.error("html2canvas error:", err); | |
alert("Canvas render failed: " + err.message); | |
google.script.host.close(); | |
}); | |
}); | |
chart.draw(data, {}); | |
} | |
</script> | |
</head> | |
<body> | |
<div id="chart" style="width: 900px; height: 500px;"></div> | |
</body> | |
</html> | |
`; | |
const htmlOutput = HtmlService.createHtmlOutput(html) | |
.setWidth(960) | |
.setHeight(600); | |
SpreadsheetApp.getUi().showModalDialog(htmlOutput, 'Generating Sankey Diagram...'); | |
} | |
function insertImageToSheet(base64) { | |
const blob = Utilities.newBlob(Utilities.base64Decode(base64), 'image/png', 'sankey.png'); | |
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet(); | |
const lastRow = sheet.getLastRow(); | |
sheet.insertImage(blob, 1, lastRow + 2); // Insert image 2 rows below the data | |
} | |
function onOpen() { | |
const ui = SpreadsheetApp.getUi(); | |
ui.createMenu('📊 Chart Tools') // You can name this anything | |
.addItem('Insert Sankey Diagram', 'drawSankey') // Menu text, function name | |
.addToUi(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment