### 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.