Visualizing Your Time Series Data with the Highcharts Library and InfluxDB

Navigate to:

How to visualize your time series data with the Highcharts Library and InfluxDB

If you’re building an IoT application on top of InfluxDB, you’ll probably use a graphing library to handle your visualization needs. Today we’re going to take a look at the charting library, Highcharts, to visualize our time series data with InfluxDB Cloud. However, I also encourage you to take a look at Giraffe, a React-based visualization library that powers the data visualizations in the InfluxDB 2.0 UI. (If you’re instead looking for a tutorial on visualizing time series data with Chart.js and InfluxDB, click the link.)

Requirements

To follow this tutorial, you’ll need to ensure you have an instance of InfluxDB up and running. This tutorial assumes that you’ve signed up for a free tier InfluxDB Cloud account, but you can install InfluxDB v2 OSS as well.

I recently published a beginner’s guide on the influxdb-client-js library which supports Node, browser, and Deno environments. It covers how to query and write data to InfluxDB with the influxdb-client-js library.  In this tutorial we’ll write, query, and visualize ocean tide data from Hanalei, Hilo, Honolulu, and Kahului using Highcharts. Run the project for yourself from this repo.

First steps in visualizing time series data with Highcharts

Pulling in the Highcharts library is our first step. I added the following script tag to the head section of the index.html file.

<script src="https://code.highcharts.com/highcharts.js"></script>

To the body of the index.html file, you’ll need a container div with an id of ‘container’ so we can later target that in the script file, like so:

<div id="container"></div>

The Highcharts graph will be rendered within this container.

In our server file, we’ve already set up an endpoint to query the data from our ocean tides database (see below) so we’ll need to fetch the data in our script file and set it into our Highcharts constructor function. You must first instantiate the queryApi and query InfluxDB with Flux with the queryRows method as described in Getting Started with JavaScript and InfluxDB 2.0.

app.get('/api/v1/tide/:place', (request, response) => {
    const { place } = request.params;
    console.log(place)
    const results = []
    queryApi.queryRows(`from(bucket:"ocean_tides") |> range(start:0) |> filter(fn: (r) => r.location =~ /(?i)(${place})/)`, {
        next(row, tableMeta) {
            const o = tableMeta.toObject(row)
                // console.log(
                //     `${o._time} ${o._measurement} in '${o.location}': ${o._field}=${o._value}`
                // )
            results.push(o)
        },
        error(error) {
            console.error(error)
            console.log('\nFinished ERROR queryRows')
        },
        complete() {
            response.status(200).json(results)
            console.log('\nFinished SUCCESS queryRows')
        },
    })
});

In the script file, this simple fetch function retrieves the data based on the location name passed in.

const fetchData = (place) => {
  return fetch(`/api/v1/tide/${place}`)
    .then(res => {
      if (res.status !== 200) {
        console.log(res);
      }
      return res;
    })
    .then(res => res.json())
    .catch(error => console.log(error));
}

Promise.all() is used to fetch all the data for the four different locations. Next, the results are mutated to fit into the required format referenced in the Highcharts documentation.

return Promise.all([
            fetchData('hilo'),
            fetchData('hanalei'),
            fetchData('honolulu'),
            fetchData('kahului')
         ])
        .then(parsedRes => {
          const mutatedArray = parsedRes.map( arr => {
            return Object.assign({}, {
              name: arr[0].location,
              data: arr.map( obj => Object.assign({}, {
                x: (moment(obj._time).unix())*1000,
                y:obj._value
              }))
            });
          });
        })
        .catch(error => console.log(error));

Now that we have successfully queried and fetched our data, we can construct our graph.

Highcharts.chart('container', {
            colors: ['#508991', '#175456', '#09BC8A', '#78CAD2'],
            chart: {
              backgroundColor: {
                  linearGradient: [0, 600, 0, 0],
                  stops: [
                    [0, 'rgb(255, 255, 255)'],
                    [1, 'rgb(161, 210, 206)']
                  ]
              },
              type: 'spline'
            },
            title: {
              text: 'Hawaii Ocean Tides',
              style: {
                'color': '#175456',
              }
            },
            xAxis: {
              type: 'datetime'
            },
            yAxis: {
              title: {
                text: 'Height (ft)'
              }
            },
            plotOptions: {
              series: {
                turboThreshold: 2000,
              }
            },
            series: mutatedArray
          });

Let’s break down this code. The chart()  method accepts two arguments:

  1. The target element within which to render the chart.
  2. An options object within which you can specify various properties such as style, title, legend, series, type, plotOptions, and so on. The options include:
    • colors: [array] – The colors property accepts an array of hex codes which will represent the default color scheme for the chart. If all colors are used up, any new colors needed will result in the array being looped through again.
    • chart: {object} – The chart property accepts an object with various additional properties including type, zoomtype, animation, events, description and a number of style properties. In this instance, I've given the background a linear gradient and designated the type as spline.
    • title: {object} – This represents the chart's main title and can be additionally given a style object to jazz things up a bit.
    • xAxis: {object} – In this scenario, because I'm using time series data, I know the x-axis will always be time so I can designate the type as 'datetime' and the scale will automatically adjust to the appropriate time unit. However, there are numerous other options here including styling, labels, custom tick placement, and logarithm or linear type.
    • yAxis: {object} – Similar to the xAxis property, the y-axis takes an object and has access to a number of options to customize the design and style of the chart's y-axis. I've only specified y-axis title in this case, and deferred to Highcharts automatic tick placement.
    • plotOptions: {object} – The plotOptions property is a wrapper object for config objects for each series type. The config objects for each series can also be overridden for an individual series item as given in the series array. Here I've used the plotOptions.series property to override the default turboThreshold of 1000 and change it to 2000. This allows for charting a greater number of data points (over the default of 1000). According to the docs, conf options for the series are accessed at three different levels. If you want to target all series in a chart, you would use the plotOptions.series object. For series of a specific type, you would access the plotOptions of that type. For instance, to target the plotOptions for a chart type of 'line' you would access the plotOptions.line object. Lastly, options for a specific series are given in the series property (see next bullet point).
    • series: [array] or {object} – This is where you'll pass in your data. You can additionally define the type for the data to be passed in, give it a name, and define additional plotOptions for it.

Here’s the final result!

Visualizing Ocean Tide IoT data with Highcharts and InfluxDB<figcaption> Visualizing Ocean Tide IoT data with Highcharts and InfluxDB</figcaption>

Next steps to visualize your time series data with the Highcharts Library and InfluxDB

The Highcharts graphing library is a deep library with seemingly endless visualization possibilities. I urge you to take a look at Highcharts documentation and demos to get inspired. If you are using InfluxDB v2 and need help, please ask for some in our community site or Slack channel. If you’re developing a cool IoT application or monitoring your application on top of InfluxDB, we’d love to hear about it, so make sure to share your story! Additionally, please share your thoughts, concerns, or questions in the comments section. We’d love to get your feedback and help you with any problems you run into!

Further reading

While this post aims to provide a comprehensive overview of how to visualize time series data from InfluxDB with Highcharts, the following resources might also interest you:

  1. Getting Started with JavaScript and InfluxDB 2.0: This post describes how to get started with the JavaScript Client Library.
  2. Write Example: This code from the JavaScript Client Library repo highlights how to write data to InfluxDB.
  3. Query Example: This code from the JavaScript Client Library repo highlights how to query data from InfluxDB.