Search code examples
heatmapechartsapache-echarts

Select a cell in a heatmap


I was looking at ECharts specifically cartesian heatmap. I want to select a particular cell in a heatmap and display its information. There is this property called selectedMode where we can set it to single for single selection, 'multiple' for multiple selection (Link to the Doc). I set it to single, but on click of a cell it selects the entire column. Can anyone help to figure out he issue here. Link to the example


Solution

  • Updated 2023-07-03

    In the future, the improvement of this is expected, as the data can also be distinguished within the original array, since each record has its own ID in the array, so it would not require a separate unique designation.

    echarts pr 18835 status Echarts PR - fix(select): fix selection key get, use id before use name



    Solution

    The reason is data without name will be assigned with a default name from category axis. So cells on the same column will have same name. This design is mainly for the bar, line charts. But it may have this kind of selection issues in the heatmaps because data with same name will be selected all.

    The solution is assign a unique name for each cell data item.

    pissang's comment

    The structure of the Echarts Heatmap series.data is as follows: series-heatmap.data

    It can be seen from here that the Echarts Heatmap accepts arrays of arrays, as you provided, and also accepts arrays of objects (as suggested by pissang). In the latter case, the sub-array, which we preserve as the value of the value key, remains the same.

    In your code, there is no "unique" name for each cell.

    const data = [
      // hour, day, value
      [0, 0, 5],
      [1, 0, 1],
      [2, 0, "-"],
      // ...
    ]
    

    Instead, you should pass objects that have two keys: name and value. The name will be the unique name, and the value will be the original value of the three axes.

    const data = [
      // name: unique, value: [hour, day, value]
      { name: "0_0", value: [0, 0, 5] },
      { name: "1_0", value: [1, 0, 1] },
      { name: "2_0", value: [2, 0, "-"] },
      // ...
    ]
    

    Since the pairing of your "z" and "x" axes is never repeated, meaning you don't have a day where you would record two values at the same time, such as 9 AM in the morning, this pairing serves as a perfect unique name. We can write a function that transforms your array accordingly.

    const data = [
      // hour, day, value
      [0, 0, 5],
      [1, 0, 1],
      [2, 0, "-"],
      // ...
    ]
    
    /**
     ** Add Name to Cells
     * - on current example, x = hour, z = day, y = value
     */
    const transform = (d) => d.map(([x, z, y]) => ({
      name: `${x}_${z}`,
      value: [x, z, y]
    }))
    
    /**
     ** Test
     */
    console.log(transform(data))

    Example

    /**
     ** Add Name to Cells
     * - on current example, x = hour, z = day, y = value
     */
    const transform = (d) => d.map(([x, z, y]) => ({
      name: `${x}_${z}`,
      value: [x, z, y]
    }))
    
    /**
     ** Initialize
     */
    const chartDom = document.getElementById('chart')
    const chart = echarts.init(chartDom)
    
    /**
     ** Data
     */
    const data = [[0, 0, 5], [0, 1, 1], [0, 2, 0], [0, 3, 0], [0, 4, 0], [0, 5, 0], [0, 6, 0], [0, 7, 0], [0, 8, 0], [0, 9, 0], [0, 10, 0], [0, 11, 2], [0, 12, 4], [0, 13, 1], [0, 14, 1], [0, 15, 3], [0, 16, 4], [0, 17, 6], [0, 18, 4], [0, 19, 4], [0, 20, 3], [0, 21, 3], [0, 22, 2], [0, 23, 5], [1, 0, 7], [1, 1, 0], [1, 2, 0], [1, 3, 0], [1, 4, 0], [1, 5, 0], [1, 6, 0], [1, 7, 0], [1, 8, 0], [1, 9, 0], [1, 10, 5], [1, 11, 2], [1, 12, 2], [1, 13, 6], [1, 14, 9], [1, 15, 11], [1, 16, 6], [1, 17, 7], [1, 18, 8], [1, 19, 12], [1, 20, 5], [1, 21, 5], [1, 22, 7], [1, 23, 2], [2, 0, 1], [2, 1, 1], [2, 2, 0], [2, 3, 0], [2, 4, 0], [2, 5, 0], [2, 6, 0], [2, 7, 0], [2, 8, 0], [2, 9, 0], [2, 10, 3], [2, 11, 2], [2, 12, 1], [2, 13, 9], [2, 14, 8], [2, 15, 10], [2, 16, 6], [2, 17, 5], [2, 18, 5], [2, 19, 5], [2, 20, 7], [2, 21, 4], [2, 22, 2], [2, 23, 4], [3, 0, 7], [3, 1, 3], [3, 2, 0], [3, 3, 0], [3, 4, 0], [3, 5, 0], [3, 6, 0], [3, 7, 0], [3, 8, 1], [3, 9, 0], [3, 10, 5], [3, 11, 4], [3, 12, 7], [3, 13, 14], [3, 14, 13], [3, 15, 12], [3, 16, 9], [3, 17, 5], [3, 18, 5], [3, 19, 10], [3, 20, 6], [3, 21, 4], [3, 22, 4], [3, 23, 1], [4, 0, 1], [4, 1, 3], [4, 2, 0], [4, 3, 0], [4, 4, 0], [4, 5, 1], [4, 6, 0], [4, 7, 0], [4, 8, 0], [4, 9, 2], [4, 10, 4], [4, 11, 4], [4, 12, 2], [4, 13, 4], [4, 14, 4], [4, 15, 14], [4, 16, 12], [4, 17, 1], [4, 18, 8], [4, 19, 5], [4, 20, 3], [4, 21, 7], [4, 22, 3], [4, 23, 0], [5, 0, 2], [5, 1, 1], [5, 2, 0], [5, 3, 3], [5, 4, 0], [5, 5, 0], [5, 6, 0], [5, 7, 0], [5, 8, 2], [5, 9, 0], [5, 10, 4], [5, 11, 1], [5, 12, 5], [5, 13, 10], [5, 14, 5], [5, 15, 7], [5, 16, 11], [5, 17, 6], [5, 18, 0], [5, 19, 5], [5, 20, 3], [5, 21, 4], [5, 22, 2], [5, 23, 0], [6, 0, 1], [6, 1, 0], [6, 2, 0], [6, 3, 0], [6, 4, 0], [6, 5, 0], [6, 6, 0], [6, 7, 0], [6, 8, 0], [6, 9, 0], [6, 10, 1], [6, 11, 0], [6, 12, 2], [6, 13, 1], [6, 14, 3], [6, 15, 4], [6, 16, 0], [6, 17, 0], [6, 18, 0], [6, 19, 0], [6, 20, 1], [6, 21, 2], [6, 22, 2], [6, 23, 6]]
        .map((item) => [item[1], item[0], item[2] || '-'])
    
    /**
     ** Option
     */
    option = {
      tooltip: {
        position: "top"
      },
      animation: false,
      grid: {
        height: "50%",
        top: "10%"
      },
      xAxis: {
        type: "category",
        data: ["12a", "1a", "2a", "3a", "4a", "5a", "6a", "7a", "8a", "9a", "10a", "11a", "12p", "1p", "2p", "3p", "4p", "5p", "6p", "7p", "8p", "9p", "10p", "11p"],
        splitArea: {
          show: true
        }
      },
      yAxis: {
        type: "category",
        data: ["Saturday", "Friday", "Thursday", "Wednesday", "Tuesday", "Monday", "Sunday"],
        splitArea: {
          show: true
        }
      },
      visualMap: {
        min: 0,
        max: 10,
        calculable: true,
        orient: "horizontal",
        left: "center",
        bottom: "15%"
      },
      series: [{
        name: "Punch Card",
        type: "heatmap",
        selectedMode: 'single',
        data: transform(data)
      }]
    }
    
    /**
     ** Render Chart
     */
    chart.setOption(option)
    <script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.4.2/echarts.min.js" integrity="sha512-VdqgeoWrVJcsDXFlQEKqE5MyhaIgB9yXUVaiUa8DR2J4Lr1uWcFm+ZH/YnzV5WqgKf4GPyHQ64vVLgzqGIchyw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    
    <div id="chart" style="width: 600px; height: 300px;"></div>