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 columnXMode: polarColumnMode,
74 dataLabels: {
75 style: {
76 fontSize: 16,
77 multiLineAlignment: EMultiLineAlignment.Center,
78 lineSpacing: 8,
79 },
80 color: "black",
81 precision: 2,
82 pointGapThreshold: 0,
83 skipNumber: 0,
84 polarLabelMode,
85 labelYPositionMode: EColumnDataLabelPosition.Position,
86 labelYPositionMultiplier: 0.5,
87 metaDataSelector: (metadata) => {
88 const md = metadata as SunburstMetadata;
89 return `${md.title} \n ${md.value}`;
90 },
91 },
92 dataSeries: createDataFn(levelData[i]),
93 strokeThickness: 2,
94 dataPointWidth,
95 dataPointWidthMode: EDataPointWidthMode.Range,
96 defaultY1: i,
97 paletteProvider: paletteProvider,
98 });
99 dataPointSelectionModifier.includeSeries(rs$, true);
100 sciChartSurface.renderableSeries.add(rs$);
101 }
102 };
103
104 const drillDownAnimationFn = (isReverse$: boolean, onCompleteFn$: () => void = () => undefined) => {
105 const xMin$ = 0; // always zero
106 const xMax0$ = prevXVisRangeDiff;
107 const xMax1$ = rootNode.value;
108
109 const startAngle0$ = isReverse$
110 ? startAngleDefault
111 : startAngleDefault + (2 * Math.PI * selectedElStartX) / xAxis.visibleRange.diff;
112 let startAngle1$: number;
113 if (isReverse$) {
114 const levelDiff$ = prevLevel - level;
115 const element$ = levelData[levelDiff$].metadata.find((a) => a.id.toString() === prevNodeId.toString());
116 startAngle1$ = startAngleDefault + (2 * Math.PI * element$.start) / rootNode.value;
117 } else {
118 startAngle1$ = startAngleDefault;
119 }
120
121 const yMin0$ = isReverse$ ? 0 : prevLevel - level;
122 const yMin1$ = isReverse$ ? level - prevLevel : 0;
123
124 const from$ = { x1: xMax0$, x2: startAngle0$, y1: yMin0$, y2: 0 };
125 const to$ = { x1: xMax1$, x2: startAngle1$, y1: yMin1$, y2: 0 };
126
127 const sweepAnimation = new GenericAnimation({
128 from: from$,
129 to: to$,
130 duration: 1000,
131 ease: easing.inOutSine,
132 onAnimate: (from, to, progress) => {
133 const xMaxCur = DoubleAnimator.interpolate(from.x1, to.x1, progress);
134 const startAngleCur = DoubleAnimator.interpolate(from.x2, to.x2, progress);
135 const yMinCur = DoubleAnimator.interpolate(from.y1, to.y1, progress);
136 const yMaxCur = yMinCur + 4;
137 xAxis.visibleRange = new NumberRange(xMin$, xMaxCur);
138 xAxis.startAngle = startAngleCur;
139 yAxis.visibleRange = new NumberRange(yMinCur, yMaxCur);
140 },
141 onCompleted: () => {
142 onCompleteFn$();
143 },
144 });
145 sciChartSurface.addAnimation(sweepAnimation);
146 };
147
148 const subscribeFn = () => {
149 dataPointSelectionModifier.selectionChanged.subscribe((args) => {
150 const selectedDataPoint = args.selectedDataPoints[0];
151 if (selectedDataPoint) {
152 const { yValue } = selectedDataPoint;
153 const md = selectedDataPoint?.metadata as SunburstMetadata;
154 const { id } = md;
155 const newId = [...id];
156 if (yValue === 1 && id.length > 1) {
157 newId.pop();
158 }
159 drawSeriesFn(
160 wasmContext,
161 xAxis,
162 yAxis,
163 sciChartSurface,
164 polarLabelMode,
165 dataPointSelectionModifier,
166 newId,
167 md.start,
168 nodeId
169 );
170 }
171 });
172 };
173
174 if (level < prevLevel) {
175 drillDownAnimationFn(true, () => {
176 redrawSeriesFn();
177 xAxis.startAngle = startAngleDefault;
178 yAxis.visibleRange = new NumberRange(0, 4);
179 subscribeFn();
180 });
181 } else if (level > prevLevel) {
182 redrawSeriesFn();
183 drillDownAnimationFn(false);
184 subscribeFn();
185 } else {
186 xAxis.startAngle = startAngleDefault;
187 xAxis.visibleRange = new NumberRange(0, rootNode.value);
188 yAxis.visibleRange = new NumberRange(0, 4);
189 redrawSeriesFn();
190 subscribeFn();
191 }
192};
193
194export const drawExample = async (rootElement: string | HTMLDivElement) => {
195 const { sciChartSurface, wasmContext } = await SciChartPolarSurface.create(rootElement, {
196 theme: appTheme.SciChartJsTheme,
197 });
198
199 const startAngle = -Math.PI / 2;
200 const totalAngle = 2 * Math.PI;
201
202 const xAxis = new PolarNumericAxis(wasmContext, {
203 isVisible: false,
204 polarAxisMode: EPolarAxisMode.Angular,
205 axisAlignment: EAxisAlignment.Top,
206 visibleRange: new NumberRange(0, 65),
207 startAngle,
208 totalAngle,
209 });
210 xAxis.polarLabelMode = EPolarLabelMode.Parallel;
211 sciChartSurface.xAxes.add(xAxis);
212
213 const yAxis = new PolarNumericAxis(wasmContext, {
214 isVisible: false,
215 polarAxisMode: EPolarAxisMode.Radial,
216 axisAlignment: EAxisAlignment.Right,
217 visibleRange: new NumberRange(0, 6),
218 flippedCoordinates: false,
219 startAngle,
220 totalAngle,
221 });
222 sciChartSurface.yAxes.add(yAxis);
223
224 const dataPointSelectionModifier = new PolarDataPointSelectionModifier({
225 allowClickSelect: true,
226 allowDragSelect: false,
227 selectionStroke: "red",
228 selectionFill: "#ff879f33",
229 });
230
231 drawSeriesFn(
232 wasmContext,
233 xAxis,
234 yAxis,
235 sciChartSurface,
236 EPolarLabelMode.Parallel,
237 dataPointSelectionModifier,
238 [0],
239 0,
240 [0]
241 );
242
243 const tooltipDataTemplate: TCursorTooltipDataTemplate = (seriesInfos: SeriesInfo[], tooltipTitle: string) => {
244 const res: string[] = [];
245 seriesInfos.forEach((si) => {
246 if (si.isHit) {
247 const md = si.pointMetadata as SunburstMetadata;
248 res.push(`Name: ${md.title}`);
249 res.push(`Value: ${md.value}`);
250 }
251 });
252 return res;
253 };
254
255 sciChartSurface.chartModifiers.add(
256 dataPointSelectionModifier,
257 new PolarCursorModifier({
258 showTooltip: true,
259 showCircularLine: false,
260 showRadialLine: false,
261 tooltipDataTemplate,
262 })
263 );
264
265 return { sciChartSurface, wasmContext };
266};
267This 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.