var page = require(‘webpage’).create();
page.content = “<div id=‘myDiagramDiv’ style=‘overflow:hidden’> <img id=‘myImg’ /></body></html>”;
// We include go.js before acting on our page, assuming it is in the same directory
page.injectJs(‘go.js’);
console.log(‘Initiated’);
page.onError = function(msg, trace) {
var msgStack = ['ERROR: ' + msg];
if (trace && trace.length) {
msgStack.push('TRACE:');
trace.forEach(function(t) {
msgStack.push(' -> ' + t.file + ': ' + t.line + (t.function ? ' (in function '' + t.function +'')' : ''));
});
}
console.error(msgStack.join('\n'));
};
page.evaluate(function() {
// GoJS is loaded, now we set up a diagram and make the image we want
var $ = go.GraphObject.make;
var myDiagram = $(go.Diagram, 'myDiagramDiv', // the ID of the DIV HTML element
{
initialContentAlignment: go.Spot.TopCenter, // center the content
initialAutoScale: go.Diagram.UniformToFill,
initialViewportSpot: go.Spot.TopCenter,
allowZoom: true
});
// console.log('Diagram Initiated');
var Inner = 60; // inner radius
var Thickness = 120; // Inner + Thickness is outer radius
// console.log('var defined');
function normalizeData(arr) {
}
myDiagram.nodeTemplate =
$(go.Node, 'Spot',
$(go.Panel,
new go.Binding('itemArray', 'values', normalizeData),
{
itemTemplate:
$(go.Panel, 'Spot',
$(go.Shape, // this always occupies the full circle
{ fill: '#00000020', stroke: 'white' },
new go.Binding('geometry', '', makeAnnularWedge),
new go.Binding('fill', 'color')),
$(go.TextBlock,
{ width: Thickness, textAlign: 'center', font: 'bold 8pt sans-serif' },
new go.Binding('alignment', '', computeTextAlignment),
new go.Binding('angle', '', ensureUpright), // does the text need to be rotated?
new go.Binding('text', '', d => d.text + '(' + (d.sweep/3.60).toFixed(1) + '%)')),
{
toolTip: $('ToolTip',
$(go.TextBlock,
new go.Binding('text', '', d => `${d.text}\n#: ${d.value}\n${(d.sweep/3.60).toFixed(1)}%`)))
}
)
}
),
$(go.TextBlock,
{ maxSize: new go.Size(2*Inner, 2*Inner), font: 'bold 10pt sans-serif' },
new go.Binding('text'))
);
function normalizeData(arr) {
let total = 0;
var categories = [];
arr.forEach(cat => {
let cattot = 0;
cat.data.forEach(det => {
if (typeof det.value !== 'number') det.value = 1;
total += det.value;
cattot += det.value;
});
cat.value = cattot;
categories.push(cat);
});
if (total <= 0) return [];
var details = [];
let angle = 270;
arr.forEach(cat => {
cat.angle = angle;
cat.sweep = (cat.value/total) * 360;
cat.radius = Inner;
cat.thick = Thickness;
cat.data.forEach(det => {
det.angle = angle;
var sw = (det.value/total) * 360;
det.sweep = sw;
angle += sw;
det.radius = Inner + Thickness;
det.thick = Thickness;
if (!det.color) det.color = cat.color;
details.push(det);
});
});
return categories.concat(details);
}
function makeAnnularWedge(data) {
var angle = (typeof data.angle === 'number') ? data.angle : 0; // start angle
var sweep = (typeof data.sweep === 'number') ? data.sweep : 90;
var radius = (typeof data.radius === 'number') ? data.radius : Inner; // inner radius
var thick = (typeof data.thick === 'number') ? data.thick : Thickness;
// the Geometry will be centered about (0,0)
var outer = radius + thick; // the outer radius
var inner = radius;
var p = new go.Point(outer, 0).rotate(angle);
var q = new go.Point(inner, 0).rotate(angle + sweep);
var rad = Inner + 2*Thickness;
var geo = new go.Geometry()
.add(new go.PathFigure(-rad, -rad)) // always make sure the Geometry includes the top left corner
.add(new go.PathFigure(rad, rad)) // and the bottom right corner of the whole circular area
.add(new go.PathFigure(p.x, p.y) // start at outer corner, go clockwise
.add(new go.PathSegment(go.PathSegment.Arc, angle, sweep, 0, 0, outer, outer))
.add(new go.PathSegment(go.PathSegment.Line, q.x, q.y)) // to opposite inner corner, then anticlockwise
.add(new go.PathSegment(go.PathSegment.Arc, angle + sweep, -sweep, 0, 0, inner, inner).close()));
return geo;
}
function computeTextAlignment(data) {
var angle = (typeof data.angle === 'number') ? data.angle : 0; // start angle
var sweep = (typeof data.sweep === 'number') ? data.sweep : 90;
var radius = (typeof data.radius === 'number') ? data.radius : Inner; // inner radius
var thick = (typeof data.thick === 'number') ? data.thick : Thickness;
var p = new go.Point(radius + thick / 2, 0).rotate(angle + sweep / 2);
return new go.Spot(0.5, 0.5, p.x, p.y);
}
// only used if rotating the text labels
function ensureUpright(data) {
var angle = (typeof data.angle === 'number') ? data.angle : 0;
var sweep = (typeof data.sweep === 'number') ? data.sweep : 90;
var a = angle + sweep/2;
if (a > 90 && a < 270) return a + 180;
return a;
}
myDiagram.model = new go.GraphLinksModel(
[
{
key: 2, text: '',
values: [
{
text: 'Yes',
data: [
{ text: 'A', value: 123 },
{ text: 'B', value: 234 },
{ text: 'C', value: 89 },
{ text: 'D', value: 45 }
],
color: '#6aaea2'
},
{
text: 'No',
data: [
{ text: 'X', value: 56 },
{ text: 'Y', value: 34 },
{ text: 'Z', value: 12 }
],
color: '#ababac'
},
{
text: 'Maybe',
data: [
{ text: '178', value: 178 }
],
color: '#547576'
}
]
},
],
[]);
var img = document.getElementById('myImg');
img.src = myDiagram.makeImageData({
scale: 1,
// PhantomJS tends to handle transparency poorly in the images it renders,
// so we prefer to use a white background:
background: 'white'
})
document.body.style.margin = '0px';
});
// We want to ensure that the image is done loading before we render
setInterval(function() {
var imgComplete = page.evaluate(function() {
return document.getElementById(‘myImg’).complete
});
if (imgComplete) {
// PhantomJS renders the entire page, and we just want to output the
,
// so we must clip to its bounding rect:
var clipRect = page.evaluate(function() {
return document.getElementById(‘myImg’).getBoundingClientRect();
});
page.clipRect = {
top: clipRect.top,
left: clipRect.left,
width: clipRect.width,
height: clipRect.height
}
page.render(‘mr.png’);
phantom.exit();
}
}, 100);