In our continuous journey to enhance the visualization capabilities of SAP Analytics Cloud (SAC), we introduce our latest creation: the SAC Chord Diagram Widget. This widget is crafted to visualize complex inter-relationships , offering a clear and concise view of data flow and connections between different entities.
But before we dive in, make sure you’ve read our previous blog post:
While SAC offers plenty visualization options, the Chord Diagram Widget is our solution to a gap in representing intricate relationships and flows between different data points in a matrix format. This widget not only integrates smoothly with SAC but also ensures an intuitive user experience right out of the box.
Have you ever found yourself entwined in a web of data, trying to understand the relationships and flows between different entities? If so, you’re not alone. Visualizing complex relationships in data can be a daunting task, but there’s a simple yet profoundly effective way to do that: the Chord Diagram.
Imagine a scenario where you have multiple countries and you want to visualize the flow of resources, communication, or dependencies between them. This is where the Chord Diagram shines. It presents an elegant and clear visualization of these relationships, allowing you to quickly grasp the interconnectedness and flow within a matrix of entities.
Let’s take an example to illustrate the utility of our Chord Diagram Widget. Each country interacts with the others, sharing resources, information, or services. In a tabular data format, you might have something like this:
Year |
Country From |
Country To |
Sales in Mio |
2021 |
CHINA |
GERMANY |
10 |
2021 |
CHINA |
USA |
20 |
2021 |
GERMANY |
CHINA |
30 |
2021 |
GERMANY |
USA |
40 |
2021 |
USA |
GERMANY |
50 |
Each row represents a directional flow from one country to another, with the “Sales” indicating the volume or strength of the flow.
When this data is fed into the Chord Diagram Widget, it transforms into a compelling visual story. Each country is represented as a segment along the circumference of a circle. Arrows (or chords) connect these segments, with the width and direction of the arrows representing the value and direction of the flow between the entities.
1. Nodes (or Groups): These are usually represented as arcs along the outer edge of the diagram. Each node represents a category (in our case, countries: China, Germany, and the USA).
2. Chords: These are the arcs inside the circle that connect two nodes. Each chord represents relationships between two categories (the flow of values from one category to another).
3. Ticks and Labels: These are placed around the outer edge of the diagram, providing a scale or labels for the nodes.
4. Nodes and Their Colors:
5.Chords and Their Directions:
– Chords should ideally show the directionality of the relationship between two nodes. This is often done by varying the width of the chord at each end, so it might be wider at the source node and narrower at the target node.
– The tick values (0, 10, 20 for China, 0 through 60 for Germany, and 0 through 40 for the USA) arerepresenting the cumulative values of the incoming and outgoing chords for each node.
This visualization allows you to instantly perceive the strongest and weakest flows between countries, understand their relationships, and identify any isolated entities or tight clusters.
The Chord Diagram is a tool that enables you to intuitively understand, explore, and analyze the complex inter-relationships within your data, providing actionable insights that can drive decision-making and strategy planning.
Before we dive into functionalities, let’s understand the widget’s code structure:
– Main JS Code (ChordDiagramWidget.js): The backbone of our widget, defining its appearance and behavior. Full JavaScript code can be viewed [here].
– JSON Configuration (ChordDiagramWidget.json): This file outlines the widget’s properties, methods, events, and data bindings. Full JSON code can be viewed [here].
The Chord Diagram Widget provides a vibrant, interactive visualization of relational data. Users can click on segments to explore data relationships, ensuring a dynamic exploration experience.
The Chord Diagram Widget expects a matrix of relationships to function optimally. The matrix can be created and maintained in the public or private dimension using SAC Planing functionality. Later in the Builder panel, the source , target and measure must be assigned as demonstrated below:
In our demo, we have kept it simple with 2 generic dimensions, 1 date dimension and one measure for demonstration purposes.
The core of the widget is defined in the `ChordDiagramWidget` class, which extends the `HTMLElement` class. The constructor initializes the widget, attaches a shadow root, sets up a resize observer, and loads the D3.js library, which is crucial for rendering the Chord Diagram.
– `onCustomWidgetBeforeUpdate`: Called before the widget updates, used to merge the current properties with the changed properties.
– `onCustomWidgetAfterUpdate`: Called after the widget updates, triggering the `_updateData` method to handle new data if the data binding changes.
– `disconnectedCallback`: Called when the widget is removed from the DOM, used to disconnect the resize observer.
The `_onResize` method is triggered when the widget’s size changes, re-rendering the chart to fit the new size.
this.resizeObserver = new ResizeObserver(() => this._onResize());
_onResize() {
console.log("Resizing Chart");
this._maybeRenderChart();
console.log("Chart Resized");
}
_maybeRenderChart() {
console.log("Checking render:", this._ready, this.currentData);
if (this._ready && this.currentData) {
this._renderChart(this.currentData);
}
}
transformToMatrix method transforms the original data into a matrix format that can be used by D3 to create the chord diagram. It extracts unique labels (countries) and initializes a matrix with zeros, then fills it based on the relationships defined in the data.
transformToMatrix(data) {
console.log("Original Data:", data);
// Extract unique labels (countries) from data
const labels = [...new Set(data.map(d => d.dimensions_0.label).concat(data.map(d => d.dimensions_1.label)))];
console.log("Labels:", labels);
// Initialize an empty matrix with zeros
const matrix = Array(labels.length).fill(null).map(() => Array(labels.length).fill(0));
// Fill the matrix based on data
data.forEach(d => {
const sourceIndex = labels.indexOf(d.dimensions_0.label);
const targetIndex = labels.indexOf(d.dimensions_1.label);
const value = d.measures_0.raw;
matrix[sourceIndex][targetIndex] = value;
});
console.log("Transformed Data:", { labels, matrix });
return {
labels,
matrix
};
The `_updateData` method is responsible for handling new data, checking the data’s state, transforming it if necessary, and then triggering the chart rendering.
async _updateData(dataBinding) {
console.log("Data Binding Received:", dataBinding);
console.log("Data available:", !!dataBinding && !!dataBinding.data);
console.log("this._ready:", this._ready);
if (this._paused) {
console.log("Widget is paused, not updating data.");
return;
}
if (dataBinding && dataBinding.data) {
const matrixData = this.transformToMatrix(dataBinding.data);
this.currentData = matrixData;
this._props.metadata = dataBinding.metadata;
console.log("Matrix Data for Rendering:", matrixData);
// Check for this._ready and call _maybeRenderChart if true
if (this._ready) {
console.log("Ready state true, attempting to render chart.");
await this._renderChart(matrixData);
}
}
}
The `_renderChart` method uses D3.js to render the Chord Diagram, setting up dimensions, defining color scales, creating the SVG container, appending the Chord arcs, and handling text labels within the arcs.
_renderChart(data) {
console.log("D3 available:", typeof d3 !== "undefined");
console.log("Rendering Chart with Data:", data);
const width = this._props.width || this.offsetWidth;
const height = this._props.height || this.offsetHeight;
const outerRadius = Math.min(width, height) * 0.5 - 40;
const innerRadius = outerRadius - 30;
.........
// Draw the arcs with transitions
svg.append("g")
.attr("class", "arcs")
.selectAll("path")
.data(chords.groups)
.enter().append("path")
.attr("fill", d => color(data.labels[d.index]))
......
// Add labels
svg.append("g")
.attr("class", "labels")
.selectAll("g")
.data(chords.groups)
.......
Data binding is pivotal in any data visualization widget as it forms the bridge between raw data and visual representation. In the Chord Diagram Widget, data binding ensures that the matrix data is accurately represented in a circular layout.
The Chord Diagram Widget is not only about data but also about presenting it in a visually appealing and informative manner. The styling plays a crucial role in making the chart both informative and aesthetically pleasing.
The `ChordDiagramWidget.json` file is a configuration file that provides SAC with all the necessary information about the Chord Diagram Widget, ensuring that SAC can seamlessly integrate and interact with the widget.
Additional Notes:
The widget uses the ResizeObserver API to detect when its size changes and to trigger a re-render of the chart.
The D3 script is loaded dynamically when the widget is connected to the DOM, and the _ready flag is set to true once it’s loaded.
The widget listens for updates to the myDataBinding property and updates the data and re-renders the chart accordingly.
The widget uses D3’s chord layout to generate the data needed to render the chord diagram and uses D3’s selection, data-binding, and transition mechanisms to render the SVG elements based on the data.
The widgets will be freely accessible on our Github page [Github]. We believe in open-source development and the power of community collaboration.
Just download the JSON file for the SAC Chord Diagram and upload it in your SAC tenant under Custom Widgets as follows:
Integrating the SAC Chord Diagram Widget into SAC is a straightforward process. Once you have the `ChordDiagramWidget.json` file ready, you can upload the widget to SAC through the Custom Widgets panel. After uploading, the SAC Widget will be available for use in your dashboards.