Search code examples
rshinyhighchartstreemap

I want to show only categories in the legend of a highchart treemap. Is there a way to do that using shiny?


I want to make a treemap to get an impression of the sizes in one data.frame. Let me show you an example:

World population plot without legend

I use the following code to produce this chart:

library(shiny)
library(highcharter)
library(gapminder)
library(dplyr)


ui <- fluidPage(
  highchartOutput("hcontainer")
)
 

server <- function(input, output){
  output$hcontainer <- renderHighchart({  
    gapminder %>%
      filter(year  == 2007) %>% 
      data_to_hierarchical(group_vars = c(continent, country), 
                                        size_var = pop,
                                        colors = c('pink','yellow','blue','green','orange','red')) %>% 
      hchart(type = "treemap"
             #showInLegend = TRUE,
             #legendType='point',
             )  
  })
}

shinyApp(ui,
         server,
         options = list(launch.browser = TRUE)
         )

I saw it is possible to create a legend by uncommenting those two lines of code in the hchart-function, but the result is not what I want:

World population plot with legend

Is there a way to tell highcharts that I only want the continents in my legend? As a less important sidenote: There seems to be a bug in the highcharts, because after clicking on the legend (which you can use to hide/make reappear countries), they change their color in the legend according to their continent:

World population plot with legend after interacting with legend


Solution

  • Use the following plugin to achieve that:

    (function(H) {
      let pick = H.pick,
        defined = H.defined,
        fireEvent = H.fireEvent
      H.wrap(H.Legend.prototype.getAllItems = function(p) {
        var allItemsFirst = [],
          allItems = [];
        this.chart.series.forEach(function(series) {
          var seriesOptions = series && series.options;
          // Handle showInLegend. If the series is linked to another series,
          // defaults to false.
          if (series && pick(seriesOptions.showInLegend, !defined(seriesOptions.linkedTo) ? void 0 : false, true)) {
            // Use points or series for the legend item depending on
            // legendType
            allItemsFirst = allItems.concat(series.legendItems ||
              (seriesOptions.legendType === 'point' ?
                series.data :
                series));
          }
        });
    
        allItemsFirst.forEach(el => {
          if (el.isVisibleInLegend) {
            allItems.push(el)
          }
        })
    
        fireEvent(this, 'afterGetAllItems', {
          allItems: allItems
        });
        return allItems;
      });
    }(Highcharts));
    

    And set isVisibleInLegend: true for each point from you expect to be in the legend.

    Highcharts.chart('container', {
      series: [{
        type: "treemap",
        layoutAlgorithm: 'squarified',
        showInLegend: true,
        legendType: 'point',
        data: [{
          id: "id_1",
          name: 'A',
          isVisibleInLegend: true
        }, {
          id: "id_2",
          name: 'A1',
          value: 2,
          parent: 'id_1',
        }, {
          id: "id_3",
          name: 'A2',
          value: 2,
          parent: 'id_1',
        }]
      }]
    });
    

    JS Demo: https://jsfiddle.net/BlackLabel/vdqtok9m/

    Extending Highcharts: https://www.highcharts.com/docs/extending-highcharts/extending-highcharts