Creating a Stacked Mountain Chart (JS)

Omar Urbano | LightningChart - Apr 10 - - Dev Community

In data visualization, stacked mountain charts are used to demonstrate how variables develop where each is stacked on top of the other.

In today's article, we will create a Stacked Mountain chart in JavaScript that uses NodeJS + LightningChart JS library.

The stacked area chart has another characteristic, it represents data as percentages, considering the highest value of the series (or series) as a reference of the 100%. The rest of the data will be stacked according to its percentage level with respect to the reference.

The chart will also be filled from the plot line to the upper level, with a limit of 100% (the reference). This is how the 100% stacked area chart can look fully plotted from the X-axis to the Y-axis.

Project

To get started, consider that the stacked mountains chart works better for visualizing large datasets and representing them as a percentage.

However, if you need to present several data series, this chart type may be best to display only a handful of series, e.g., the most important categories of your dataset.

This chart can be edited online too

stacked-mountain-chart-editor

Template Setup

To initialize the project, let's set up a working template and follow along the tutorial. You'll see a file tree like this one:

stacked-mountain-chart-file-tree

Then open a new terminal and as usual in a Node JS project, run the NPM Install command. That'd be it, let's code.

Creating the chart

Today the most recent versions are LightningChart JS 5.1.0 and XYData 1.4.0. I recommend that you review the most recent versions and update them. This is because some LightningChart JS tools do not exist in previous versions.

In the project’s package.json file you can find the LightningChart JS dependencies:

"dependencies": {
"@arction/lcjs": "^5.1.0",
"@arction/xydata": "^1.4.0",
"webgl-obj-loader": "^2.0.8",
}
Enter fullscreen mode Exit fullscreen mode

1) Importing libraries
We will start by importing the necessary libraries to create our chart.

const lcjs = require('@arction/lcjs')
import data from './data.json'

// Extract required parts from LightningChartJS.
const { lightningChart, AxisTickStrategies, AutoCursorModes, Themes } = lcjs
Enter fullscreen mode Exit fullscreen mode

2) Add license key (free)
Once the LightningChart JS libraries are installed, we will import them into our chart.ts file. Note you will need a trial license, which is free. We would then add it to a variable that will be used for creating the chart object.

3) Properties

// Create a XY Chart.
const xyChart = lightningChart({license: license})
    .ChartXY({
        theme: Themes.cyberSpace,
    })
Enter fullscreen mode Exit fullscreen mode

Theme: defines the look and feel of your JavaScript stacked bar chart. Note that you must specify the color theme of the chart components beforehand.

setTitle: Sets the name at the top of the chart.

stacked-mountain-chart-setTitle

Setting up axes

xyChart.getDefaultAxisX()
    .setTickStrategy(AxisTickStrategies.DateTime)
    .setInterval({
        start: new Date(2017, 0, 1).getTime(),
        end: new Date(2018, 3, 1).getTime()
    })
    .setMouseInteractions(false)
Enter fullscreen mode Exit fullscreen mode

The getDefaultAxes function allows us to access all the axes of the object. For each axis, we can apply the same properties, or if we need specific properties for each one, we can access each of them by using Chart3D.getDefaultAxisX(), Chart3D.getDefaultAxisY(), Chart3D.getDefaultAxisZ().

The setTickStrategy function defines the positioning and formatting logic of Axis ticks and the style of created ticks. In this case, we use the DateTime format for our axes. In the styles collection, we can use number, time, time, and date or void.

setInterval sets the axis scale interval. Intervals are number type, for that reason, we are using the getTime() function. This transforms the date object to a number (milliseconds).
• start: (Number) Start scale value.
• end: (Number) End scale value.

SetMouseInteractions sets if mouse and cursor interactions should be disabled during zooming animations for the chart's series.

Adding the series

// ---- Add multiple series with different names and values. ----
const versionName = [
    'Version 1',
    'Version 2',
    'Version 3',
    'Version 4',
    'Version 5',
    'Version 6',
    'Version 7',
    'Version 8',
    'Version 9',
    'Version 10',
    'Version 11',
    'Version 12',
]
// Array to store the created Area Series.
const version = []
// Create a Series for each version name.
versionName.forEach((v, k) => {
    // The first version (data) is drawn at the bottom of the chart, so we can just use an Area Series to render it.
    if (k == 0) {
        version[k] = xyChart.addAreaSeries().setName(v)
    } else {
        //The rest of the versions (data) are drawn based on the version before, so we'll use Area Range Series to render it.
        version[k] = xyChart.addAreaRangeSeries().setName(v)
    }
    // Set up how to display the Result Table.
    version[k].setCursorResultTableFormatter((builder, series, xValue, yValueHigh, yValueLow) => {
        return builder
            .addRow(v)
            .addRow('Date: ' + series.axisX.formatValue(xValue))
            .addRow('Distribution: ' + (yValueHigh - yValueLow).toFixed(2) + '%')
    })
})
Enter fullscreen mode Exit fullscreen mode

Now we need to create a series for each version. All the versions are stored in the versionName array object. The addAreaSeries method adds a new AreaSeries to the chart.

This series type visualizes areas between a static baseline and supplied curve data. Note that the AreaSeries is optimized for handling large amounts of data.

The addAreaRangeSeries method adds a new AreaRangeSeries to the chart. This series type is used for visualizing bands of data between two curves of data.

Creating cursor result tables

The result cursor tables are the boxes that appear above the cursor when we position it on a coordinate of the chart. These tables can contain a specific format.

stacked-mountain-chart-cursorResultsTable

version[k].setCursorResultTableFormatter((builder, series, xValue, yValueHigh, yValueLow) => {
        return builder
            .addRow(v)
            .addRow('Date: ' + series.axisX.formatValue(xValue))
            .addRow('Distribution: ' + (yValueHigh - yValueLow).toFixed(2) + '%')
    })
Enter fullscreen mode Exit fullscreen mode
  • setCursorResultTableFormatter: This property configures the format of the CursorResultTable when pointing at this series (on-hover). The result table will be shown as a popup with the values that we want to show. Each line is generated as a row (.addrow), and we can format it freely.

Setting up data points

data[0].forEach((point, i) => {
    version.forEach((series, index) => {
        // For the first series, only one Y value is needed.
        if (index == 0) {
            version[index].add({
                x: point.x,
                y: point.y,
            })
            // Rest of the series need both the High and Low values;
            // Low is the previous Series' High value.
        } else {
            version[index].add({
                position: point.x,
                high: getYHigh(index, i),
                low: getYLow(index, i),
            })
        }
    })
Enter fullscreen mode Exit fullscreen mode

For each data point we have in the JSON file, we will have a process to configure the values of this point in each version series. For the first version, we don’t need the high and low values. For the rest of the versions, we need to get the previous and next values (Low-High). This will help us stack each version on top of each other.

Conclusion

Finally, execute the npm start command to run the chart and open the localhost path to visualize the application on your browser.

This chart is a tool that stacks each group on a percentage basis to the total, making a relative contribution so that its limit is 100%. This allows us to observe how each group of values participates in our dataset analysis.

Visually, it allows us to understand the importance of each category within the dataset. Perhaps it is not as complex as other chart types we have seen, but its objective is just the opposite, it tries to show us categories and their relevance in the analysis.

On the LightningChart JS side, we used once more an XY object, accessed the axes, configured, and added the series to the chart. The most complex part was configuring the values obtained from the JSON. LightningChart JS makes it easy to implement each chart type. Thanks!


Written by:
Omar Urbano | Software Engineer & Technical Writer
Send me your questions via LinkedIn

. . . . . . . . . . . . . . . .