6

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>
Kim
  • 4,593
  • 5
  • 21
  • 18
  • I just spent days worth of my spare time wrestling with this same issue. Ever get anywhere? – Methodician Feb 11 '22 at 14:45
  • I also stumbled upon this issue. As said on echarts' Github, they didn't want to add this feature at first but are now considering adding it (still not done as of today) [link to the feature](https://github.com/apache/echarts/issues/15488) – A mere dev Apr 25 '22 at 15:35

2 Answers2

7

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: ${myChart.getVisual({ seriesIndex: index }, 'color')}"></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"
  }
]

option = {
  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: ${myChart.getVisual({ seriesIndex: index }, 'color')}"></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: ${myChart.getVisual({ seriesIndex: index }, 'color')}"></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

The reduce() methods are slow on large datasets as they go through the whole series. For example, replacing it with binary search (dichotomic search) will drastically improve the performance.

I'd be interested if anyone has an idea to make it even more performant.

A mere dev
  • 1,263
  • 4
  • 14
0

Excellent !

One option that may interest you: you can get the color from the graph:

${myChart.getVisual({ seriesIndex: index }, 'color')}

This way you don't need to use the color array.

Clint
  • 1
  • This does not provide an answer to the question. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/late-answers/32123386) – Gabe Jul 02 '22 at 18:17
  • Indeed not an answer to the original question, it would have been a good comment / edit to my answer. Anyway, I improved my answer using your piece of code. – A mere dev Jul 19 '22 at 09:53