Creates a JavaScript Polar Sunburst Chart using SciChart.js, with multiple levels and interaction driven animations.
Also Known As: Radial Treemap, Circular Treemap.
drawExample.ts
index.html
vanilla.ts
theme.ts
sunburstData.ts
SunburstMetadata.ts
SunburstPaletteProvider.ts
1import {
2 PolarColumnRenderableSeries,
3 PolarNumericAxis,
4 SciChartPolarSurface,
5 EPolarAxisMode,
6 NumberRange,
7 EAxisAlignment,
8 EPolarLabelMode,
9 PolarCursorModifier,
10 TCursorTooltipDataTemplate,
11 SeriesInfo,
12 PolarDataPointSelectionModifier,
13 TSciChart,
14 EColumnMode,
15 XyxDataSeries,
16 EMultiLineAlignment,
17 EColumnDataLabelPosition,
18 EDataPointWidthMode,
19 GenericAnimation,
20 easing,
21 DoubleAnimator,
22} from "scichart";
23import { appTheme } from "../../../theme";
24import { SunburstMetadata } from "./SunburstMetadata";
25import { SunburstPaletteProvider } from "./SunburstPaletteProvider";
26import { getDataById, getElementById, TLevelDataForChart } from "./sunburstData";
27
28const drawSeriesFn = (
29 wasmContext: TSciChart,
30 xAxis: PolarNumericAxis,
31 yAxis: PolarNumericAxis,
32 sciChartSurface: SciChartPolarSurface,
33 polarLabelMode: EPolarLabelMode,
34 dataPointSelectionModifier: PolarDataPointSelectionModifier,
35 nodeId: number[],
36 selectedElStartX: number,
37 prevNodeId: number[]
38) => {
39 const startAngleDefault = -Math.PI / 2;
40
41 const clearSeriesFn = () => {
42 dataPointSelectionModifier.selectionChanged.unsubscribeAll();
43 sciChartSurface.renderableSeries.asArray().forEach((rs) => rs.delete());
44 sciChartSurface.renderableSeries.clear();
45 };
46
47 const level = nodeId.length - 1;
48 const prevLevel = prevNodeId.length - 1;
49 const prevXVisRangeDiff = xAxis.visibleRange.diff;
50
51 const polarColumnMode = EColumnMode.StartEnd;
52 const dataPointWidth = 0.5;
53 const paletteProvider = new SunburstPaletteProvider();
54
55 const createDataFn = (input$: TLevelDataForChart) => {
56 const [xValues$, yValues$, x1Values$] = input$.data;
57 return new XyxDataSeries(wasmContext, {
58 xValues: xValues$,
59 yValues: yValues$,
60 x1Values: x1Values$,
61 metadata: input$.metadata,
62 });
63 };
64
65 const levelData = getDataById(nodeId);
66 const rootNode = getElementById(nodeId);
67
68 const redrawSeriesFn = () => {
69 clearSeriesFn();
70 for (let i = 0; i < levelData.length; i++) {
71 const rs$ = new PolarColumnRenderableSeries(wasmContext, {
72 stroke: "black",
73 // Without an explicit fill the series inherits the theme's fillPalette entry, whose alpha
74 // (0x77 in the light theme) scales the colours the SunburstPaletteProvider returns and
75 // washes the sectors out. Any opaque fill works - the palette provider replaces it per point.
76 fill: "#FFFFFFFF",
77 columnXMode: polarColumnMode,
78 dataLabels: {
79 style: {
80 fontSize: 16,
81 multiLineAlignment: EMultiLineAlignment.Center,
82 lineSpacing: 8,
83 },
84 color: "black",
85 precision: 2,
86 pointGapThreshold: 0,
87 skipNumber: 0,
88 polarLabelMode,
89 labelYPositionMode: EColumnDataLabelPosition.Position,
90 labelYPositionMultiplier: 0.5,
91 metaDataSelector: (metadata) => {
92 const md = metadata as SunburstMetadata;
93 return `${md.title} \n ${md.value}`;
94 },
95 },
96 dataSeries: createDataFn(levelData[i]),
97 strokeThickness: 2,
98 dataPointWidth,
99 dataPointWidthMode: EDataPointWidthMode.Range,
100 defaultY1: i,
101 paletteProvider: paletteProvider,
102 });
103 dataPointSelectionModifier.includeSeries(rs$, true);
104 sciChartSurface.renderableSeries.add(rs$);
105 }
106 };
107
108 const drillDownAnimationFn = (isReverse$: boolean, onCompleteFn$: () => void = () => undefined) => {
109 const xMin$ = 0; // always zero
110 const xMax0$ = prevXVisRangeDiff;
111 const xMax1$ = rootNode.value;
112
113 const startAngle0$ = isReverse$
114 ? startAngleDefault
115 : startAngleDefault + (2 * Math.PI * selectedElStartX) / xAxis.visibleRange.diff;
116 let startAngle1$: number;
117 if (isReverse$) {
118 const levelDiff$ = prevLevel - level;
119 const element$ = levelData[levelDiff$].metadata.find((a) => a.id.toString() === prevNodeId.toString());
120 startAngle1$ = startAngleDefault + (2 * Math.PI * element$.start) / rootNode.value;
121 } else {
122 startAngle1$ = startAngleDefault;
123 }
124
125 const yMin0$ = isReverse$ ? 0 : prevLevel - level;
126 const yMin1$ = isReverse$ ? level - prevLevel : 0;
127
128 const from$ = { x1: xMax0$, x2: startAngle0$, y1: yMin0$, y2: 0 };
129 const to$ = { x1: xMax1$, x2: startAngle1$, y1: yMin1$, y2: 0 };
130
131 const sweepAnimation = new GenericAnimation({
132 from: from$,
133 to: to$,
134 duration: 1000,
135 ease: easing.inOutSine,
136 onAnimate: (from, to, progress) => {
137 const xMaxCur = DoubleAnimator.interpolate(from.x1, to.x1, progress);
138 const startAngleCur = DoubleAnimator.interpolate(from.x2, to.x2, progress);
139 const yMinCur = DoubleAnimator.interpolate(from.y1, to.y1, progress);
140 const yMaxCur = yMinCur + 4;
141 xAxis.visibleRange = new NumberRange(xMin$, xMaxCur);
142 xAxis.startAngle = startAngleCur;
143 yAxis.visibleRange = new NumberRange(yMinCur, yMaxCur);
144 },
145 onCompleted: () => {
146 onCompleteFn$();
147 },
148 });
149 sciChartSurface.addAnimation(sweepAnimation);
150 };
151
152 const subscribeFn = () => {
153 dataPointSelectionModifier.selectionChanged.subscribe((args) => {
154 const selectedDataPoint = args.selectedDataPoints[0];
155 if (selectedDataPoint) {
156 const { yValue } = selectedDataPoint;
157 const md = selectedDataPoint?.metadata as SunburstMetadata;
158 const { id } = md;
159 const newId = [...id];
160 if (yValue === 1 && id.length > 1) {
161 newId.pop();
162 }
163 drawSeriesFn(
164 wasmContext,
165 xAxis,
166 yAxis,
167 sciChartSurface,
168 polarLabelMode,
169 dataPointSelectionModifier,
170 newId,
171 md.start,
172 nodeId
173 );
174 }
175 });
176 };
177
178 if (level < prevLevel) {
179 drillDownAnimationFn(true, () => {
180 redrawSeriesFn();
181 xAxis.startAngle = startAngleDefault;
182 yAxis.visibleRange = new NumberRange(0, 4);
183 subscribeFn();
184 });
185 } else if (level > prevLevel) {
186 redrawSeriesFn();
187 drillDownAnimationFn(false);
188 subscribeFn();
189 } else {
190 xAxis.startAngle = startAngleDefault;
191 xAxis.visibleRange = new NumberRange(0, rootNode.value);
192 yAxis.visibleRange = new NumberRange(0, 4);
193 redrawSeriesFn();
194 subscribeFn();
195 }
196};
197
198export const drawExample = async (rootElement: string | HTMLDivElement) => {
199 const { sciChartSurface, wasmContext } = await SciChartPolarSurface.create(rootElement, {
200 theme: appTheme.SciChartJsTheme,
201 });
202
203 const startAngle = -Math.PI / 2;
204 const totalAngle = 2 * Math.PI;
205
206 const xAxis = new PolarNumericAxis(wasmContext, {
207 isVisible: false,
208 polarAxisMode: EPolarAxisMode.Angular,
209 axisAlignment: EAxisAlignment.Top,
210 visibleRange: new NumberRange(0, 65),
211 startAngle,
212 totalAngle,
213 });
214 xAxis.polarLabelMode = EPolarLabelMode.Parallel;
215 sciChartSurface.xAxes.add(xAxis);
216
217 const yAxis = new PolarNumericAxis(wasmContext, {
218 isVisible: false,
219 polarAxisMode: EPolarAxisMode.Radial,
220 axisAlignment: EAxisAlignment.Right,
221 visibleRange: new NumberRange(0, 6),
222 flippedCoordinates: false,
223 startAngle,
224 totalAngle,
225 });
226 sciChartSurface.yAxes.add(yAxis);
227
228 const dataPointSelectionModifier = new PolarDataPointSelectionModifier({
229 allowClickSelect: true,
230 allowDragSelect: false,
231 selectionStroke: "red",
232 selectionFill: "#ff879f33",
233 });
234
235 drawSeriesFn(
236 wasmContext,
237 xAxis,
238 yAxis,
239 sciChartSurface,
240 EPolarLabelMode.Parallel,
241 dataPointSelectionModifier,
242 [0],
243 0,
244 [0]
245 );
246
247 const tooltipDataTemplate: TCursorTooltipDataTemplate = (seriesInfos: SeriesInfo[], tooltipTitle: string) => {
248 const res: string[] = [];
249 seriesInfos.forEach((si) => {
250 if (si.isHit) {
251 const md = si.pointMetadata as SunburstMetadata;
252 res.push(`Name: ${md.title}`);
253 res.push(`Value: ${md.value}`);
254 }
255 });
256 return res;
257 };
258
259 sciChartSurface.chartModifiers.add(
260 dataPointSelectionModifier,
261 new PolarCursorModifier({
262 showTooltip: true,
263 showCircularLine: false,
264 showRadialLine: false,
265 tooltipDataTemplate,
266 })
267 );
268
269 return { sciChartSurface, wasmContext };
270};
271This example demonstrates how to create an interactive Polar Sunburst Chart using SciChart.js in JavaScript. The chart visualizes hierarchical data through concentric rings of PolarColumnRenderableSeries, where each segment represents a node in the tree structure.
The implementation processes hierarchical JSON data into level-wise flat arrays using getDataById(). Each level is rendered as a separate PolarColumnRenderableSeries with angular size corresponding to value and radial distance encoding depth. The chart uses GenericAnimation for smooth drill-down transitions between hierarchy levels.
The example features interactive drill-down/up functionality via PolarDataPointSelectionModifier. Custom SunburstPaletteProvider dynamically adjusts segment colors based on selection state. Data labels display node metadata using a custom formatter.
The chart showcases efficient memory management by properly disposing series during navigation. The async initialization pattern ensures optimal performance when loading the WebAssembly context.

Explore the React Polar Line Chart example to create data labels, line interpolation, gradient palette stroke and startup animations. Try the SciChart Demo.

Try the JavaScript Polar Spline Line Chart example to see SciChart's GPU-accelerated rendering in action. Choose a cubic spline or polar interpolation. View demo.

Create a JavaScript Multi-Cycle Polar Chart to plot data over multiple cycles and visualize patterns over time. This example shows surface temperature by month.

Try the JavaScript Polar Column or Bar Chart example to render bars in a polar layout with gradient fills and animations. Use SciChart for seamless integrations.

Create a JavaScript Polar Colum Category chart visualizing UK consumer price changes. Try the demo with a custom positive/negative threshold fill and stroke.

Create a JavaScript Polar Range Column Chart with SciChart. This example displays monthly minimum and maximum temperatures within a Polar layout. Try the demo.

View the JavaScript Windrose Chart example to display directional data with stacked columns in a polar layout. Try the polar chart demo with customizable labels

View the JavaScript Radial Column Chart example to see the difference that SciChart has to offer. Switch radial and angular axes and add interactive modifiers.

This JavaScript Stacked Radial Bar Chart example shows Olympic medal data by country. Try the demo for yourself with async initialization and theme application.

The JavaScript Polar Area Chart example, also known as Nightingale Rose Chart, renders an area series with polar coordinates with interactive legend controls.

Try the JavaScript Stacked Radial Mountain Chart example to show multiple datasets on a polar layout with a stacked mountain series and animated transitions.

Create a JavaScript Polar Chart with regular and interpolated error bands. Enhance a standard chart with shaded areas to show upper and lower data boundaries.

Build a JavaScript Polar Scatter Chart with this example to render multiple scatter series on radial and angular axes. Try the flexible SciChart demo today.

View the JavaScript Polar Radar Chart example. Also known as the Spider Radar Chart, view the scalability and stability that SciChart has to offer. Try demo.

Create JavaScript Gauge Charts, including a JavaScript Circular Gauge Dashboard, with user-friendly initialization and responsive design. Give SciChart a go.

View JavaScript Arc Gauge Charts alongside FIFO Scrolling Charts, all on the same dashboard with real-time, high-performance data rendering. Try the demo.

Try SciChart's JavaScript Polar Heatmap example to combine a polar heatmap with a legend component. Supports responsive design and chart and legend separation.

No description available for this example yet

Create a JavaScript Polar Partial Arc that bends from a full Polar Circle to a Cartesian-like arc. Try the demo to display an arc segment with Polar coordinates.

Create a JavaScript Polar Axis Label with SciChart. This demo shows the various label modes for Polar Axes – all optimised for pan, zoom, and mouse wheel.

View the React Polar Map Example using the SciChartReact component. Display geographic data as color-coded triangles on a polar coordinate system. Try demo.