'How can I show all series in tooltip when series does not have same time values

I have a Chart that shows multiple time series. The different time series does not sample at the same time. Is there a way I can show all series in the tooltip? In the example, you can see that all series are included in the tooltip for the 2 first points as they are sampled at the same time. For the rest of the points, only 1 series is included.

var myChart = echarts.init(document.getElementById('main'));

var series = [{
    "name": "sensor 1",
    "data": [{
        "value": [
          "2019-02-20T11:47:44.000Z",
          22.2
        ]
      },
      {
        "value": [
          "2019-02-20T12:03:02.000Z",
          22.1
        ]
      },
      {
        "value": [
          "2019-02-20T12:18:19.000Z",
          22.15
        ]
      },
      {
        "value": [
          "2019-02-20T12:33:36.000Z",
          22.2
        ]
      },
      {
        "value": [
          "2019-02-20T12:48:53.000Z",
          22.15
        ]
      }
    ],
    "type": "line"
  },
  {
    "name": "sensor 2",
    "data": [{
        "value": [
          "2019-02-20T11:47:44.000Z",
          23.2
        ]
      },
      {
        "value": [
          "2019-02-20T12:03:02.000Z",
          23.1
        ]
      },
      {
        "value": [
          "2019-02-20T12:22:19.000Z",
          24.15
        ]
      },
      {
        "value": [
          "2019-02-20T12:39:36.000Z",
          21.2
        ]
      },
      {
        "value": [
          "2019-02-20T12:52:53.000Z",
          20.15
        ]
      }
    ],
    "type": "line"
  }
]

var option = {
  legend: {},
  tooltip: {
    trigger: 'axis',
  },
  xAxis: {
    type: 'time'
  },
  yAxis: {
    scale: true
  },
  series: series,
};

myChart.setOption(option);
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/4.0.4/echarts.min.js"></script>

<div id="main" style="width: 500px;height:400px;"></div>


Solution 1:[1]

Solution explanation

As stated in this feature request on echarts' github, they plan to add what you're looking for in the future. But for now, it is still not supported.

So I found a workaround to have the tooltip display all series even if they don't have a value at the exact x where the axisPointer is. To do so, I used the tooltip formatter that can be defined as a callback function that is called every time the tooltip has to be changed (i.e. every time the axisPointer moves on a new value) and where you can specify your own tooltip format.

Inside this function, you have access to every piece of information about the data at the axisPointer (especially its xAxis value in our case). Given the xAxis value of the axisPointer, we can go through our series and find the closest value from that xAxis value.

formatter : (params) => {
   //The datetime where the axisPointer is
   var xTime = new Date(params[0].axisValue)
      
   //Create our custom tooltip and add to its top the dateTime where the axisPointer is
   let tooltip = `<p>${xTime.toLocaleString()}</p> `;
      
   //Go through each serie
   series.forEach((serie, index) => {
     //Find the closest value
     value = serie.data.reduce((prev, curr) => Math.abs(new Date(curr.value[0]).valueOf() - xTime.valueOf()) < Math.abs(new Date(prev.value[0]).valueOf() - xTime.valueOf()) ? curr : prev).value[1]
        
     /* Add a line in our custom tooltip */
     // Add the colored circle at the begining of the line
     tooltip += `<p><span style="display:inline-block;margin-right:5px;border-radius:10px;width:9px;height:9px;background-color: ${colors[index]}"></span>`
     // Add the serie's name and its value
     tooltip += `${serie.name} &emsp;&emsp; <b>${value}</b></p>`;
   });
   return tooltip;
}

Full code

Here is the full code, using your example :

var myChart = echarts.init(document.getElementById('main'));

var series = [{
    "name": "sensor 1",
    //step: "end",
    "data": [{
        "value": [
          "2019-02-20T11:47:44.000Z",
          22.2
        ]
      },
      {
        "value": [
          "2019-02-20T12:03:02.000Z",
          22.1
        ]
      },
      {
        "value": [
          "2019-02-20T12:18:19.000Z",
          22.15
        ]
      },
      {
        "value": [
          "2019-02-20T12:33:36.000Z",
          22.2
        ]
      },
      {
        "value": [
          "2019-02-20T12:48:53.000Z",
          22.15
        ]
      }
    ],
    "type": "line"
  },
  {
    "name": "sensor 2",
    //step: 'end',
    "data": [{
        "value": [
          "2019-02-20T11:47:44.000Z",
          23.2
        ]
      },
      {
        "value": [
          "2019-02-20T12:03:02.000Z",
          23.1
        ]
      },
      {
        "value": [
          "2019-02-20T12:22:19.000Z",
          24.15
        ]
      },
      {
        "value": [
          "2019-02-20T12:39:36.000Z",
          21.2
        ]
      },
      {
        "value": [
          "2019-02-20T12:52:53.000Z",
          20.15
        ]
      }
    ],
    "type": "line"
  }
]

//Used to 'recreate' the tooltip
var colors = [
    "#5470c6",
    "#91cc75",
    "#fac858",
    "#ee6666",
    "#73c0de",
    "#3ba272",
    "#fc8452",
    "#9a60b4",
    "#ea7ccc",
  ],


option = {
  color: colors,
  legend: {},
  tooltip: {
    trigger: 'axis',
    formatter : (params) => {
      //The datetime where the axisPointer is
      var xTime = new Date(params[0].axisValue)
      
      //Create our custom tooltip and add to its top the dateTime where the axisPointer is
      let tooltip = `<p>${xTime.toLocaleString()}</p> `;
      
      //Go through each serie
      series.forEach((serie, index) => {
        //Find the closest value
        value = serie.data.reduce((prev, curr) => Math.abs(new Date(curr.value[0]).valueOf() - xTime.valueOf()) < Math.abs(new Date(prev.value[0]).valueOf() - xTime.valueOf()) ? curr : prev).value[1]
        
        /* Add a line in our custom tooltip */
        // Add the colored circle at the begining of the line
        tooltip += `<p><span style="display:inline-block;margin-right:5px;border-radius:10px;width:9px;height:9px;background-color: ${colors[index]}"></span>`
        // Add the serie's name and its value
        tooltip += `${serie.name} &emsp;&emsp; <b>${value}</b></p>`;
      });
      return tooltip;
    }
  },
  xAxis: {
    type: 'time'
  },
  yAxis: {
    scale: true
  },
  series: series,
};

myChart .setOption(option)
<html>
  <body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.3.2/echarts.min.js"></script>
    <div id="main" style="width: 600px; height:400px;"></div>
  </body>
</html>

Further thoughts

  • Linear interpolation

Instead of displaying the value of the closest real point, we could also calculate the value with a simple linear interpolation.

Linear interpolation
Here is the formatter function with linear interpolation (not the most preformant, but working)

formatter : (params) => {
   var xTime = new Date(params[0].axisValue)
   let tooltip = `<p>${xTime.toLocaleString()}</p> `;
   series.forEach((serie, index) => {
     //Only works if series is chronologically sorted
     prev_point = serie.data.reduce((prev, curr) => new Date(curr.value[0]).valueOf() <= xTime.valueOf() ? curr : prev)
     next_point = serie.data.slice(0).reduce((prev, curr, i, arr) => {
       if(new Date(curr.value[0]).valueOf() >= xTime.valueOf()) {
         arr.splice(1);
       }
       return curr
     })
     var value = 0
     if(next_point.value[1] == prev_point.value[1]){
       value = next_point.value[1]
     }
     else {
       //Linear interpolation
       value = Math.round((prev_point.value[1] + (xTime.valueOf()/1000 - new Date(prev_point.value[0]).valueOf()/1000) * ((next_point.value[1] - prev_point.value[1]) / (new Date(next_point.value[0]).valueOf()/1000 - new Date(prev_point.value[0]).valueOf()/1000)))*10)/10
     }
     tooltip += `<p><span style="display:inline-block;margin-right:5px;border-radius:10px;width:9px;height:9px;background-color: ${colors[index]}"></span> ${serie.name} &emsp;&emsp; <b>${value}</b></p>`;
   });
   return tooltip;
}
  • Display the series as steps

To make it visually more accurate, you can display the series as step: 'end' and get the closest previous value instead of just the closest value, using:

value = serie.data.reduce((prev, curr) => new Date(curr.value[0]).valueOf() <= xTime.valueOf() ? curr : prev).value[1]

Doing so, the value displayed in the tooltip will be exactly what you see on the graph.

  • Performance

I don't know how this will behave on very large dataset but it could be a bit slow as it has to go through the whole series. I'd be interested if anyone has an idea to make it more performant.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1