### Install TreatmentPatterns Package
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/README.md
Install the TreatmentPatterns package from GitHub. Ensure R environment is configured with RTools and Java.
```r
install.packages("remotes")
remotes::install_github("mi-erasmusmc/TreatmentPatterns")
```
--------------------------------
### Sunburst Plot HTML Structure and Styling
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/sunburst_standalone.html
Basic HTML and CSS setup for the Sunburst plot, defining layout, fonts, and element styles. This is the foundational structure for the visualization.
```html
Sunburst Plot
```
--------------------------------
### Get Tooltip Direction for Sunburst Arc
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/sunburst_standalone.html
Determines the tooltip direction relative to a sunburst arc. Currently set to 'n' (north).
```javascript
getTipDirection(d) {
return("n");
}
```
--------------------------------
### Initialization and Refresh Logic
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/legend.html
Sets up the Chart and Sunburst objects, defines the data and lookup, and includes a refresh function to re-render the plot when data changes. It also integrates the node splitting logic.
```javascript
window.onload = function () { var chrt = new Chart(); var plot = new Sunburst(); var target = document.querySelector('#plot'); function split(node) { if (isNaN(node.data.name)) { return [node]; }; let splitNodes = [...Number.parseInt(node.data.name).toString(2)].reverse().reduce((result, bit, i) => { if (bit == "1") { let nodeClone = Object.assign({}, node); nodeClone.data = {name: (1< { node.y0 = node.y0 + (i * bandWidth); node.y1 = node.y0 + bandWidth; return node; }) } function refreshPlot() { chartData = JSON.parse(document.querySelector("#chartData").value); plot.render(chartData.data, target, 700,700, {split: split}, chartData.lookup); } refreshPlot(); };
```
--------------------------------
### Generic Tooltip Factory
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/sunburst_shiny.html
Creates a tooltip function that formats data points into HTML strings for display. Allows configuration of labels, accessors, and formatters for each tooltip item.
```javascript
tooltipFactory(tooltips) {
return (d) => {
let tipText = '';
if (tooltips !== undefined) {
for (let i = 0; i < tooltips.length; i = i + 1) {
let value = tooltips[i].accessor(d);
if (tooltips[i].format !== undefined) {
value = tooltips[i].format(value);
}
tipText += `${tooltips[i].label}: ${value}`;
}
}
return tipText;
};
}
```
--------------------------------
### Sunburst Chart Class Initialization
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/sunburst_standalone.html
Defines the Chart class with static properties for chart types and methods for rendering, option management, SVG creation, and tooltip handling. Includes a debounced resize handler.
```javascript
var lodash = _;
var d3tip = d3.tip;
class Chart {
static get chartTypes() {
return {
AREA: 'AREA',
BOXPLOT: 'BOXPLOT',
DONUT: 'DONUT',
HISTOGRAM: 'HISTOGRAM',
LINE: 'LINE',
TRELLISLINE: 'TRELLISLINE',
};
}
static render(data, target, w, h, chartOptions) {
if (typeof target == "string") {
target = document.querySelector(target);
}
if (!this.doResize) {
this.doResize = lodash.debounce(() => {
this.render(data, target, target.clientWidth, target.clientHeight, chartOptions);
}, 250);
window.addEventListener("resize", this.doResize);
}
}
getOptions(chartSpecificDefaults, customOptions) {
const options = Object.assign({}, {
margins: {
top: 10,
right: 10,
bottom: 10,
left: 10,
},
xFormat: d3.format(',.0f'),
yFormat: d3.format('s'),
colors: d3.scaleOrdinal(d3.schemeCategory20.concat(d3.schemeCategory20)),
}, // clone objects
Object.assign({}, chartSpecificDefaults),
Object.assign({}, customOptions)
);
return options;
}
createSvg(target, width, height) {
this.destroyTipIfExists();
const container = d3.select(target);
container.select('svg').remove();
const chart = container.append('svg')
.attr('preserveAspectRatio', 'xMinYMin meet')
.attr('viewBox', ` 0 0 ${width} ${height}`)
.append('g')
.attr('class', 'chart');
this.chart = chart;
return chart;
}
useTip(tooltipConfigurer = () => {}, options) {
this.destroyTipIfExists();
this.tip = d3tip()
.attr('class', 'd3-tip');
tooltipConfigurer(this.tip, options);
if (this.chart) {
this.chart.call(this.tip);
}
return this.tip;
}
destroyTipIfExists() {
if (this.tip) {
this.tip.destroy();
}
}
static normalizeDataframe(dataframe) {
// rjson serializes dataframes with 1 row as single element properties.
// This function ensures fields are always arrays.
const keys = d3.keys(dataframe);
const frame = Object.assign({}, dataframe);
keys.forEach((key) => {
if (!(dataframe[key] instanceof Array)) {
frame[key] = [dataframe[key]];
}
});
return frame;
}
static dataframeToArray(dataframe) {
// dataframes from R serialize into an obect where each column is an array of values.
```
--------------------------------
### Tooltip Factory for Custom Tooltips
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/sunburst_standalone.html
Generates a tooltip function based on a provided array of tooltip configurations. Each configuration specifies a label, accessor, and optional formatter.
```javascript
tooltipFactory(tooltips) {
return (d) => {
let tipText = '';
if (tooltips !== undefined) {
for (let i = 0; i < tooltips.length; i = i + 1) {
let value = tooltips[i].accessor(d);
if (tooltips[i].format !== undefined) {
value = tooltips[i].format(value);
}
tipText += `${tooltips[i].label}: ${value}`;
}
}
return tipText;
};
}
```
--------------------------------
### SI Formatting Utility
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/sunburst_shiny.html
Provides a formatter for SI (International System of Units) prefixes, suitable for displaying large or small numbers concisely. Handles rounding to a specified precision.
```javascript
get formatters() {
return {
formatSI: (p) => {
p = p || 0;
return (d) => {
if (d < 1) {
return Math.round(d, p);
}
const prefix = d3.format(',.0s', d);
return prefix(d);
}
},
};
}
```
--------------------------------
### Sunburst Chart Initialization and Data Refresh
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/sunburst_standalone.html
Initializes the Sunburst chart and sets up a function to refresh the plot with new data. Includes a custom 'split' function for handling specific data structures.
```javascript
window.onload = function () {
var chrt = new Chart();
var plot = new Sunburst();
var target = document.querySelector('#plot');
function split(node) {
if (isNaN(node.data.name)) {
return [node];
};
let splitNodes = [...Number.parseInt(node.data.name).toString(2)].reverse().reduce((result, bit, i) => {
if (bit == "1") {
let nodeClone = Object.assign({}, node);
nodeClone.data = {name: (1< {
node.y0 = node.y0 + (i * bandWidth);
node.y1 = node.y0 + bandWidth;
return node;
})
}
function refreshPlot() {
chartData = JSON.parse(document.querySelector("#chartData").value);
plot.render(chartData.data, target, 700,700, {split: split}, chartData.lookup);
}
refreshPlot();
};
```
--------------------------------
### SI Formatter Utility
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/sunburst_standalone.html
Creates a formatter function for SI (International System of Units) prefixes. Useful for displaying large or small numbers concisely.
```javascript
get formatters() {
return {
formatSI: (p) => {
p = p || 0;
return (d) => {
if (d < 1) {
return Math.round(d, p);
}
const prefix = d3.format(',.0s', d);
return prefix(d);
}
},
};
}
```
--------------------------------
### Prepare Chart Data
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/sunburst_shiny.html
Prepares raw data for different chart types, specifically handling boxplot data by mapping min, max, median, and quartile values. Returns null if data is insufficient.
```javascript
static prepareData(rawData, chartType) {
switch (chartType) {
case this.chartTypes.BOXPLOT:
if (!rawData.CATEGORY.length) {
return null;
}
const data = rawData.CATEGORY.map((d,i) => ({
Category: rawData.CATEGORY[i],
min: rawData.MIN_VALUE[i],
max: rawData.MAX_VALUE[i],
median: rawData.MEDIAN_VALUE[i],
LIF: rawData.P10_VALUE[i],
q1: rawData.P25_VALUE[i],
q3: rawData.P75_VALUE[i],
UIF: rawData.P90_VALUE[i],
}), rawData);
const values = Object.values(data);
const flattenData = values.reduce((accumulator, currentValue) => accumulator.concat(currentValue), []);
if (!flattenData.length) {
return null;
}
return data;
}
}
```
--------------------------------
### Map Month-Year Data to Series Format
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/sunburst_standalone.html
Transforms raw data containing month-year information into a series format suitable for charting. Assumes specific field names for date, y-value, and y-percentage.
```javascript
static mapMonthYearDataToSeries(data, customOptions) {
const defaults = {
dateField: 'x',
yValue: 'y',
yPercent: 'p'
};
const options = Object.assign({}, defaults, customOptions );
const series = {};
series.name = 'All Time';
series.values = [];
data[options.dateField].map((datum, i) => {
series.values.push({
xValue: new Date(Math.floor(data[options.dateField][i] / 100), (data[options.dateField][i] % 100) - 1, 1),
yValue: data[options.yValue][i],
yPercent: data[options.yPercent][i]
});
});
series.values.sort((a, b) => a.xValue - b.xValue);
return [series]; // return series wrapped in an array
}
```
--------------------------------
### Sunburst Chart Rendering Logic
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/sunburst_shiny.html
This snippet initializes the D3 hierarchy, partitions the data, and renders the sunburst chart. It handles node filtering, color mapping, and event listeners for tooltips and clicks. It also includes logic for splitting nodes based on options.
```javascript
const root = d3.hierarchy(data)
.sum(function (d) { return d.size; })
.sort(function (a, b) { return b.value - a.value; });
let nodes = partition(root).descendants().filter(d => (d.x1 - d.x0 > 0.005)).reverse(); // 0.005 radians = 0.29 degrees
function search(nameKey){
for (var i=0; i < lookup.length; i++) {
if (lookup[i].key === nameKey) {
return lookup[i].value;
}
}
}
var colors = {};
for (var i=0; i < lookup.length; i++) {
if (lookup[i].value == 'End') {
colors[lookup[i].value] = "#FFFFFF" ;
} else {
colors[lookup[i].value] = options.colors(lookup[i].value);
}
}
if (options.split) {
const multiNodes = nodes.reduce((result, node) => {
let splitNodes = options.split(node);
if (splitNodes.length > 1) {
node.isSplit = true;
result = result.concat(splitNodes.map(n => Object.assign(n, { isPartialNode: true })));
}
return result;
},[]);
// append arcs, but do not apply tooltips, and only look for 'partial' nodes to select
vis.data([data]).selectAll("partialnode")
.data(multiNodes)
.enter()
.append("svg:path")
.attr("d", arc)
.attr("fill-rule", "evenodd")
.attr("class", "partial")
.style("fill", function(d) { return colors[search(d.data.name)]; })
}
const self = this; // append arcs and tooltips
vis.data([data]).selectAll("pathnode")
.data(nodes)
.enter()
.append("svg:path")
.attr("display", function (d) { return d.depth ? null : "none"; })
.attr("d", arc)
.attr("fill-rule", "evenodd")
.attr("class", d => (options.nodeClass && options.nodeClass(d)) || "node")
.style("fill", d => d.isSplit ? "#000" : colors[search(d.data.name)])
.style("opacity", d => d.isSplit ? 0 : 1)
.on('mouseover', d => self.tip.show(Object.assign({}, d, { tipDirection: self.getTipDirection(d), tipOffset: self.getTipOffset(d, arc)}), event.target))
.on('mouseout', d => self.tip.hide(d, event.target))
.on('click', (d) => options.onclick && options.onclick(d));
this.drawLegend(colors);
}
```
--------------------------------
### Sunburst Chart Rendering Logic
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/sunburst_standalone.html
Renders the Sunburst chart, including creating the SVG container, setting up tooltips, processing hierarchical data, and drawing arcs. Customization options like splitting nodes and defining click handlers are supported.
```javascript
render(data, target, width, height, chartOptions, lookup) {
super.render(data, target, width, height, chartOptions);
const defaultOptions = {
tooltip: (d) => {
return '' //`
No Tooltip Set
`
}
};
// options
const options = this.getOptions(defaultOptions, chartOptions);
// container
const svg = this.createSvg(target, width, height);
svg.attr('class', 'sunburst')
// this must be done after createSvg()
this.useTip((tip, options) => {
tip.attr('class', `d3-tip ${options.tipClass || ""}`)
.offset(d => d.tipOffset || [-10,0])
.direction(d => d.tipDirection || "n")
.html(d => options.tooltip(d))
}, options);
const vis = svg.append("svg:g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
const radius = Math.min(width, height) / 2;
const partition = d3.partition()
.size([2 * Math.PI, radius]);
const arc = d3.arc()
.startAngle(function (d) { return d.x0; })
.endAngle(function (d) { return d.x1; })
.innerRadius(function (d) { return d.y0 })
.outerRadius(function (d) { return d.y1 });
vis.append("svg:circle")
.attr("r", radius)
.style("opacity", 0);
// Turn the data into a d3 hierarchy and calculate the sums.
const root = d3.hierarchy(data)
.sum(function (d) { return d.size; })
.sort(function (a, b) { return b.value - a.value; });
let nodes = partition(root).descendants().filter(d => (d.x1 - d.x0 > 0.005)).reverse(); // 0.005 radians = 0.29 degrees
function search(nameKey){
for (var i=0; i < lookup.length; i++) {
if (lookup[i].key === nameKey) {
return lookup[i].value;
}
}
}
var colors = {};
for (var i=0; i < lookup.length; i++) {
if (lookup[i].value == 'End') {
colors[lookup[i].value] = "#FFFFFF" ;
} else {
colors[lookup[i].value] = options.colors(lookup[i].value);
}
}
if (options.split) {
const multiNodes = nodes.reduce((result, node) => {
let splitNodes = options.split(node);
if (splitNodes.length > 1) {
node.isSplit = true;
result = result.concat(splitNodes.map(n => Object.assign(n, { isPartialNode: true })));
}
return result;
}, []);
// append arcs, but do not apply tooltips, and only look for 'partial' nodes to select
vis.data([data]).selectAll("partialnode")
.data(multiNodes)
.enter()
.append("svg:path")
.attr("d", arc)
.attr("fill-rule", "evenodd")
.attr("class", "partial")
.style("fill", function(d) { return colors[search(d.data.name)]; });
}
const self = this;
// append arcs and tooltips
vis.data([data]).selectAll("pathnode")
.data(nodes)
.enter()
.append("svg:path")
.attr("display", function (d) { return d.depth ? null : "none"; })
.attr("d", arc)
.attr("fill-rule", "evenodd")
.attr("class", d => (options.nodeClass && options.nodeClass(d)) || "node")
.style("fill", d => d.isSplit ? "#000" : colors[search(d.data.name)])
.style("opacity", d => d.isSplit ? 0 : 1)
.on('mouseover', d => self.tip.show(Object.assign({}, d, { tipDirection: self.getTipDirection(d), tipOffset: self.getTipOffset(d, arc)}), event.target))
.on('mouseout', d => self.tip.hide(d, event.target))
.on('click', (d) => options.onclick && options.onclick(d));
this.drawLegend(colors);
}
```
--------------------------------
### Prepare Data for Boxplot Chart
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/sunburst_standalone.html
Prepares raw data for a boxplot chart. It maps categorical data with min, max, median, and quartile values into a structured format.
```javascript
static prepareData(rawData, chartType) {
switch (chartType) {
case this.chartTypes.BOXPLOT:
if (!rawData.CATEGORY.length) {
return null;
}
const data = rawData.CATEGORY.map((d,i) => ({
Category: rawData.CATEGORY[i],
min: rawData.MIN_VALUE[i],
max: rawData.MAX_VALUE[i],
median: rawData.MEDIAN_VALUE[i],
LIF: rawData.P10_VALUE[i],
q1: rawData.P25_VALUE[i],
q3: rawData.P75_VALUE[i],
UIF: rawData.P90_VALUE[i]
}), rawData);
const values = Object.values(data);
const flattenData = values.reduce((accumulator, currentValue) => accumulator.concat(currentValue), []);
if (!flattenData.length) {
return null;
}
return data;
}
}
```
--------------------------------
### Sunburst Chart Constants and Rendering Class
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/legend.html
Defines chart types and the main Chart class for rendering Sunburst visualizations. Includes methods for resizing, option configuration, SVG element creation, and tooltip management.
```javascript
var lodash = ; var d3tip = d3.tip; class Chart {
static get chartTypes() {
return {
AREA: 'AREA',
BOXPLOT: 'BOXPLOT',
DONUT: 'DONUT',
HISTOGRAM: 'HISTOGRAM',
LINE: 'LINE',
TRELLISLINE: 'TRELLISLINE',
};
}
static render(data, target, w, h, chartOptions) {
if (typeof target == "string") {
target = document.querySelector(target);
}
if (!this.doResize) {
this.doResize = lodash.debounce(() => {
this.render(data, target, target.clientWidth, target.clientHeight, chartOptions);
}, 250);
window.addEventListener("resize", this.doResize);
}
}
getOptions(chartSpecificDefaults, customOptions) {
const options = Object.assign({}, {
margins: {
top: 10,
right: 10,
bottom: 10,
left: 10,
},
xFormat: d3.format(',.0f'),
yFormat: d3.format('s'),
colors: d3.scaleOrdinal(d3.schemeCategory20.concat(d3.schemeCategory20)),
}, // clone objects
Object.assign({}, chartSpecificDefaults),
Object.assign({}, customOptions)
);
return options;
}
createSvg(target, width, height) {
this.destroyTipIfExists();
const container = d3.select(target);
container.select('svg').remove();
const chart = container.append('svg')
.attr('preserveAspectRatio', 'xMinYMin meet')
.attr('viewBox', ` 0 0 ${width} ${height}`)
.append('g')
.attr('class', 'chart');
this.chart = chart;
return chart;
}
useTip(tooltipConfigurer = () => {}, options) {
this.destroyTipIfExists();
this.tip = d3tip()
.attr('class', 'd3-tip');
tooltipConfigurer(this.tip, options);
if (this.chart) {
this.chart.call(this.tip);
}
return this.tip;
}
destroyTipIfExists() {
if (this.tip) {
this.tip.destroy();
}
}
static normalizeDataframe(dataframe) {
// rjson serializes dataframes with 1 row as single element properties.
// This function ensures fields are always arrays.
const keys = d3.keys(dataframe);
const frame = Object.assign({}, dataframe);
keys.forEach((key) => {
if (!(dataframe[key] instanceof Array)) {
frame[key] = [dataframe[key]];
}
});
return frame;
}
static dataframeToArray(dataframe) {
// dataframes from R serialize into an obect where each column is an array of values.
```
--------------------------------
### Dispose Chart Resources
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/sunburst_standalone.html
Cleans up chart resources, including removing any existing tooltips and detaching the window resize event listener if it was set.
```javascript
dispose() {
this.destroyTipIfExists();
if (this.doResize) {
window.removeEventListener("resize", this.doResize);
}
}
```
--------------------------------
### Default Donut Chart Tooltip Generator
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/sunburst_standalone.html
Generates a default tooltip for donut charts, displaying the label, value, and percentage for a given data point.
```javascript
donutDefaultTooltip(labelAccessor, valueAccessor, percentageAccessor) {
return (d) => `${labelAccessor(d)}: ${valueAccessor(d)} (${percentageAccessor(d)})`
}
```
--------------------------------
### Prepare Boxplot Data
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/legend.html
Transforms raw data into a format suitable for boxplot charts. It extracts category, min, max, median, quartiles, and percentiles.
```javascript
static prepareData(rawData, chartType) {
switch (chartType) {
case this.chartTypes.BOXPLOT:
if (!rawData.CATEGORY.length) {
return null;
}
const data = rawData.CATEGORY.map((d,i) => ({
Category: rawData.CATEGORY[i],
min: rawData.MIN_VALUE[i],
max: rawData.MAX_VALUE[i],
median: rawData.MEDIAN_VALUE[i],
LIF: rawData.P10_VALUE[i],
q1: rawData.P25_VALUE[i],
q3: rawData.P75_VALUE[i],
UIF: rawData.P90_VALUE[i]
}), rawData);
const values = Object.values(data);
const flattenData = values.reduce((accumulator, currentValue) => accumulator.concat(currentValue), []);
if (!flattenData.length) {
return null;
}
return data;
}
}
```
--------------------------------
### Default Line Chart Tooltip Generator
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/sunburst_standalone.html
Creates a default tooltip function for line charts. It displays series name, x-axis label with formatted value, and y-axis label with formatted value.
```javascript
lineDefaultTooltip( xLabel, xFormat, xAccessor, yLabel, yFormat, yAccessor, seriesAccessor ) {
return (d) => {
let tipText = '';
if (seriesAccessor(d)) tipText = `Series: ${seriesAccessor(d)}`;
tipText += `${xLabel}: ${xFormat(xAccessor(d))}`;
tipText += `${yLabel}: ${yFormat(yAccessor(d))}`;
return tipText;
}
}
```
--------------------------------
### Sunburst Tooltip Direction and Offset
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/legend.html
Determines the tooltip direction ('n' for north) and calculates the offset for a sunburst chart arc based on the event target's bounding box and the arc's centroid.
```javascript
getTipDirection(d) {
return("n");
}
getTipOffset(d, arc) {
const bbox = event.target.getBBox();
const arcCenter = arc.centroid(d);
let tipOffsetX = Math.abs(bbox.x - arcCenter[0]) - (bbox.width/2)
let tipOffsetY = Math.abs(bbox.y - arcCenter[1]);
return([tipOffsetY-10,tipOffsetX]);
}
```
--------------------------------
### Calculate Tooltip Offset for Sunburst Arc
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/sunburst_standalone.html
Calculates the offset for a tooltip relative to a sunburst arc's centroid and the bounding box of the target element.
```javascript
getTipOffset(d, arc) {
const bbox = event.target.getBBox();
const arcCenter = arc.centroid(d);
let tipOffsetX = Math.abs(bbox.x - arcCenter[0]) - (bbox.width/2)
let tipOffsetY = Math.abs(bbox.y - arcCenter[1]);
return([tipOffsetY-10,tipOffsetX]);
}
```
--------------------------------
### Sunburst Tooltip Offset Calculation
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/sunburst_shiny.html
Calculates the offset for a tooltip in a Sunburst chart based on the bounding box of the target element and the centroid of the arc. Adjusts for positioning.
```javascript
getTipOffset(d, arc) {
const bbox = event.target.getBBox();
const arcCenter = arc.centroid(d);
let tipOffsetX = Math.abs(bbox.x - arcCenter[0]) - (bbox.width/2)
let tipOffsetY = Math.abs(bbox.y - arcCenter[1]);
return([tipOffsetY-10,tipOffsetX]);
}
```
--------------------------------
### Render Sunburst Chart
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/sunburst_shiny.html
Renders a sunburst chart using D3.js within a target element. It handles chart options, tooltips, and SVG element creation, preparing the data for partitioning and arc generation.
```javascript
render(data, target, width, height, chartOptions, lookup) { super.render(data, target, width, height, chartOptions); const defaultOptions = { tooltip: (d) => { return '' //`
No Tooltip Set
` } }; // options const options = this.getOptions(defaultOptions, chartOptions); // container const svg = this.createSvg(target, width, height); svg.attr('class', 'sunburst') // this must be done after createSvg() this.useTip((tip, options) => { tip.attr('class', `d3-tip ${options.tipClass || ""}`) .offset(d => d.tipOffset || [-10,0]) .direction(d => d.tipDirection || "n") .html(d => options.tooltip(d)) }, options); const vis = svg.append("svg:g") .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); const radius = Math.min(width, height) / 2; const partition = d3.partition() .size([2 * Math.PI, radius]); const arc = d3.arc() .startAngle(function (d) { return d.x0; }) .endAngle(function (d) { return d.x1; }) .innerRadius(function (d) { return d.y0 }) .outerRadius(function (d) { return d.y1 }); vis.append("svg:circle") .attr("r", radius) .style("opacity", 0); // Turn the data into a d3 hierarchy and calculate the sums.
```
--------------------------------
### PDF Export Functionality
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/sunburst_shiny.html
This function generates a PDF document from the sunburst chart SVG elements. It iterates through SVG elements, converts them to PDF using SVGtoPDF, and handles the PDF stream and blob creation for download or display.
```javascript
function resetDefaultStyles(doc) {
doc.fillColor('black')
.fillOpacity(1)
.strokeColor('black')
.strokeOpacity(1)
.lineWidth(1)
.undash()
.fontSize(12)
.font('Helvetica');
}
function printFunction(outFileName) {
console.log('print to: ' + outFileName)
let doc = new PDFDocument({compress: false});
let svgs = document.querySelectorAll('#plot > svg');
for (let i = 0; i < svgs.length; i++) {
doc.fontSize(20).text(svgs[i].id, {underline: true});
SVGtoPDF(doc, svgs[i].outerHTML || svgs[i], 0, 0);
if (i !== svgs.length - 1) {
resetDefaultStyles(doc);
doc.addPage();
}
}
let stream = doc.pipe(blobStream());
stream.on('finish', function() {
let blob = stream.toBlob('application/pdf');
if (navigator.msSaveOrOpenBlob) {
navigator.msSaveOrOpenBlob(blob, outFileName);
} else {
document.getElementById('pdf-file').style.display = "block";
document.getElementById('pdf-file').setAttribute('src', URL.createObjectURL(blob));
}
});
doc.end();
}
```
--------------------------------
### Chart Class Definition
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/sunburst_shiny.html
Defines the Chart class with static properties for chart types and methods for rendering, option management, SVG creation, and tooltip handling. It also includes utility methods for data normalization.
```javascript
var lodash = rimenti; var d3tip = d3.tip; class Chart {
static get chartTypes() {
return {
AREA: 'AREA',
BOXPLOT: 'BOXPLOT',
DONUT: 'DONUT',
HISTOGRAM: 'HISTOGRAM',
LINE: 'LINE',
TRELLISLINE: 'TRELLISLINE',
};
}
render(data, target, w, h, chartOptions) {
if (typeof target == "string") {
target = document.querySelector(target);
}
if (!this.doResize) {
this.doResize = lodash.debounce(() => {
this.render(data, target, target.clientWidth, target.clientHeight, chartOptions);
}, 250);
window.addEventListener("resize", this.doResize);
}
}
getOptions(chartSpecificDefaults, customOptions) {
const options = Object.assign({}, {
margins: { top: 10, right: 10, bottom: 10, left: 10 },
xFormat: d3.format(',.0f'),
yFormat: d3.format('s'),
colors: d3.scaleOrdinal(d3.schemeCategory20.concat(d3.schemeCategory20)),
}, // clone objects
Object.assign({}, chartSpecificDefaults),
Object.assign({}, customOptions)
);
return options;
}
createSvg(target, width, height) {
this.destroyTipIfExists();
const container = d3.select(target);
container.select('svg').remove();
const chart = container.append('svg')
.attr('preserveAspectRatio', 'xMinYMin meet')
.attr('viewBox', ` 0 0 ${width} ${height}`)
.append('g')
.attr('class', 'chart');
this.chart = chart;
return chart;
}
useTip(tooltipConfigurer = () => {}, options) {
this.destroyTipIfExists();
this.tip = d3tip()
.attr('class', 'd3-tip');
tooltipConfigurer(this.tip, options);
if (this.chart) {
this.chart.call(this.tip);
}
return this.tip;
}
destroyTipIfExists() {
if (this.tip) {
this.tip.destroy();
}
}
static normalizeDataframe(dataframe) {
// rjson serializes dataframes with 1 row as single element properties.
// This function ensures fields are always arrays.
const keys = d3.keys(dataframe);
const frame = Object.assign({}, dataframe);
keys.forEach((key) => {
if (!(dataframe[key] instanceof Array)) {
frame[key] = [dataframe[key]];
}
});
return frame;
}
static dataframeToArray(dataframe) {
// dataframes from R serialize into an obect where each column is an array of values.
```
--------------------------------
### Node Splitting Logic for Sunburst Chart
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/legend.html
Defines a function to split a Sunburst chart node into multiple nodes based on its binary representation. This is used for visualizing data where a single node might represent multiple states.
```javascript
function split(node) { if (isNaN(node.data.name)) { return [node]; }; let splitNodes = [...Number.parseInt(node.data.name).toString(2)].reverse().reduce((result, bit, i) => { if (bit == "1") { let nodeClone = Object.assign({}, node); nodeClone.data = {name: (1< { node.y0 = node.y0 + (i * bandWidth); node.y1 = node.y0 + bandWidth; return node; }) }
```
--------------------------------
### Default Line Chart Tooltip
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/sunburst_shiny.html
Generates a tooltip function specifically for line charts. Formats and displays series name, X-axis value, and Y-axis value based on provided accessors and formatters.
```javascript
lineDefaultTooltip( xLabel, xFormat, xAccessor, yLabel, yFormat, yAccessor, seriesAccessor ) {
return (d) => {
let tipText = '';
if (seriesAccessor(d)) tipText = `Series: ${seriesAccessor(d)}`;
tipText += `${xLabel}: ${xFormat(xAccessor(d))}`;
tipText += `${yLabel}: ${yFormat(yAccessor(d))}`;
return tipText;
}
}
```
--------------------------------
### Convert DataFrame to Array of Objects
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/sunburst_standalone.html
Converts a DataFrame-like object into an array of objects. Handles cases where data is structured as arrays per column or as a single object.
```javascript
const keys = d3.keys(dataframe);
let result;
if (dataframe[keys[0]] instanceof Array) {
result = dataframe[keys[0]].map((d, i) => {
const item = {};
keys.forEach(p => {
item[p] = dataframe[p][i];
});
return item;
});
} else {
result = [dataframe];
}
return result;
```
--------------------------------
### Sunburst Chart Legend Rendering
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/sunburst_standalone.html
Renders a color legend for the Sunburst chart based on provided color mappings. This function is typically called internally by the chart's render method.
```javascript
var li = { w: 300, h: 30, s: 3, r: 3 };
var legend = d3.select("#legend").append("svg:svg")
.attr("width", li.w)
.attr("height", d3.keys(colors).length * (li.h + li.s));
var g = legend.selectAll("g")
.data(d3.entries(colors))
.enter().append("svg:g")
.attr("transform", function(d, i) { return "translate(0," + i * (li.h + li.s) + ")"; });
g.append("svg:rect")
.attr("rx", li.r)
.attr("ry", li.r)
.attr("width", li.w)
.attr("height", li.h)
.style("fill", function(d) { return d.value; });
g.append("svg:text")
.attr("x", li.w / 2)
.attr("y", li.h / 2)
.attr("dy", "0.35em")
.attr("text-anchor", "middle")
.text(function(d) { return d.key; });
```
--------------------------------
### Draw Sunburst Legend
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/sunburst_standalone.html
Placeholder function for drawing the legend of a sunburst chart. It takes color scales as input.
```javascript
drawLegend(colors) {
// Dimensions of legend item: width, height, spacing, radius of rounded rect.
}
```
--------------------------------
### Draw Sunburst Legend
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/sunburst_shiny.html
Placeholder function for drawing the legend of a Sunburst chart. Includes comments for dimensions of legend items.
```javascript
drawLegend(colors) {
// Dimensions of legend item: width, height, spacing, radius of rounded rect.
}
```
--------------------------------
### Create Sunburst Legend
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/sunburst_shiny.html
Generates a legend for the sunburst chart based on color mappings. This function appends SVG elements to a specified container to display color swatches and their corresponding labels.
```javascript
var li = { w: 300, h: 30, s: 3, r: 3 }; var legend = d3.select("#legend").append("svg:svg") .attr("width", li.w) .attr("height", d3.keys(colors).length * (li.h + li.s)); var g = legend.selectAll("g") .data(d3.entries(colors)) .enter().append("svg:g") .attr("transform", function(d, i) { return "translate(0," + i * (li.h + li.s) + ")"; }); g.append("svg:rect") .attr("rx", li.r) .attr("ry", li.r) .attr("width", li.w) .attr("height", li.h) .style("fill", function(d) { return d.value; }); g.append("svg:text") .attr("x", li.w / 2) .attr("y", li.h / 2) .attr("dy", "0.35em") .attr("text-anchor", "middle") .text(function(d) { return d.key; });
```
--------------------------------
### Wrap Text into Multiple Lines
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/sunburst_standalone.html
Wraps SVG text elements into multiple lines if they exceed a specified width. Optionally truncates lines after a certain number and adds a title with the full text.
```javascript
wrap(text, width, truncateAtLine) {
text.each(function () {
const text = d3.select(this);
const fullText = text.text();
const words = text.text().split(/\s+/).reverse();
let line = [];
let word;
let lineNumber = 0;
let lineCount = 0;
const lineHeight = 1.1; // ems
const y = text.attr('y');
const dy = parseFloat(text.attr('dy'));
let tspan = text
.text(null)
.append('tspan')
.attr('x', 0)
.attr('y', y)
.attr('dy', `${dy}em`);
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(' '));
if (tspan.node().getComputedTextLength() > width) {
if (line.length > 1) {
line.pop(); // remove word from line
words.push(word); // put the word back on the stack
const text = !!truncateAtLine && ++lineCount === truncateAtLine ? `${line.splice(0, line.length - 1).join(' ')}...` : line.join(' ');
tspan.text(text);
}
line = [];
tspan = text
.append('tspan')
.attr('x', 0)
.attr('y', y)
.attr('dy', `${++lineNumber * lineHeight + dy}em`);
if (!!truncateAtLine && truncateAtLine === lineCount) {
tspan.remove();
break;
}
}
}
text.append('title').text(fullText);
});
}
```
--------------------------------
### Truncate Text with Ellipsis
Source: https://github.com/mi-erasmusmc/treatmentpatterns/blob/master/inst/shiny/sunburst/sunburst_standalone.html
Truncates text within SVG elements if it exceeds a specified width, appending an ellipsis. Adds a title attribute with the original text for tooltips.
```javascript
truncate(text, width) {
text.each(function() {
const t = d3.select(this);
const originalText = t.text();
let textLength = t.node().getComputedTextLength();
let text = t.text();
while (textLength > width && text.length > 0) {
text = text.slice(0, -1);
t.text(`${text}...`);
textLength = t.node().getComputedTextLength();
}
t.append('title').text(originalText);
});
}
```
=== COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.