Problem with Choropleth map's legend created by plotly in R based on categorical-data and sf map

Viewed 289

Please help ! This question is very similar to this one that has been answered quite some time ago. However, I still cannot get my head around the solution:

How to create a chloropleth map in R Plotly based on a Categorical variable?

I'm trying to create an interactive Choropleth map in R for my shiny app based on categorical data, using plotly and sf data get from GADM. Here is a reproducible example:

library(raster)
library(tidyverse)
library(plotly)
library(sf)

# Get the map data in sf format
map_data <- getData("GADM", country = "FRA", level = 2, type = "sf")

# Transform sf data to modern crs object to avoid further warning message
st_crs(map_data) <- st_crs(map_data)

# Generate some random data for each region
department <- map %>% as.data.frame() %>% .[, 13]

set.seed(10, sample.kind="Rounding") 
data <- sample(x = 0:1200, size = length(department), replace = T)

map_dat <- data.frame(department = department,
                  data = data)

# Assign Class as categories
map_dat <- map_dat %>% 
  mutate(Class = cut(data,
                     breaks = c(-Inf, 50, 100, 200, 500, 1000, Inf),
                     labels = c("< 50", "50 - 100", "100 - 200",
                                "200 - 500", "500 - 1000", "> 1000")))

# Join data and plot
plot_dat <- map %>% as.data.frame() %>%
  left_join(map_dat, by = c("HASC_2" = "department")) %>%
  st_as_sf()

plot_ly(plot_dat) %>%
  add_sf(type = "scatter", 
         stroke = I("transparent"), 
         span = I(1), 
         alpha = 1,
         split = ~NAME_2,
         color = ~Class,
         colors = "Reds",
         text = ~paste0(NAME_2, "\n", data),
         hoveron = "fills",
         hoverinfo = "text") %>%
  config(displayModeBar = F)

The problem that I have with the default legend is that it's too detail and cumbersome in some way, as I just want to display a small and compact box filled with categorical class that I assign, similar to the one in ggplot. I've try to split the map with categorical Class and the legend looks a little bit better, however my hover text does not work anymore, and still, I have no idea how to edit or change the style of the legend in order for it to look decent and neat, like to change the key symbols to become a circle or a square like we usually see in a legend map box.

plot_dat %>% plot_ly() %>%
  add_sf(type = "scatter", 
         stroke = I("transparent"), 
         span = I(1), 
         alpha = 1,
         split = ~Class,
         color = ~Class,
         colors = "Reds",
         text = ~paste0(NAME_1, "\n", Count),
         hoveron = "fills",
         hoverinfo = "text") %>%
  config(displayModeBar = F) %>%
  layout(showlegend = F)

I've read through the documentation for plotly's Choropleth map in R and yet found no documentaion for categorical case (unlike Python). As I'm running out of options, my question is, is there any way to achive my desired goal here ? How can I create a proper legend box, or a bar for categorical data ?

Apologies if my question is not that clear. I'm eager to answer any questions if anyone has. Thank you in advance.

1 Answers

After getting it to work, I can't help but think...there has to be a better way.

This uses the library spPlot. This isn't a Cran package. Use the following to get this one.

devtools::install_github("GegznaV/spPlot")

You'll likely run into the same dependency issue I ran into. You will need the package ChemometricsWithR to get spPlot. It will try to install that package, but it's another one that you need to get through alternative means.

devtools::install_github("rwehrens/ChemometricsWithR")

I'm using the first plot_ly call you made. I added a few things: legendgroup = ~Class, name = ~Class, and showlegend = F. Then I piped in the function plotly_modify_legend to add the grouped legend.

(plt <- plot_ly(plot_dat) %>%
    add_sf(type = "scatter", 
           stroke = I("transparent"), 
           span = I(1), 
           alpha = 1,
           split = ~NAME_2,
           legendgroup = ~Class, # group legends together by class
           name = ~Class,        # so region names aren't shown in the legend
           color = ~Class,
           showlegend = F,       # don't show a legend for each region
           colors = "Reds",
           text = ~paste0(NAME_2, "\n", data),
           hoveron = "fills",
           hoverinfo = "text") %>%
    config(displayModeBar = F) %>%  
    plotly_modify_legend(showlegend = T, traceorder = "grouped")) # group legend visible

Plotly orders the legend in the order in which the elements appear, which is inherently alphabetical. That equates to having a really odd order in the legend.

enter image description here

The 96 separate subplots (one for each region) needed to be reordered to fix this. I could have just found one of each and changed the first 6 (one for each legend group), but I didn't think that would be easier.

Instead, I captured the traces, extracted their assigned group, and reordered them. After that, I replaced the traces in the plotly object.

# extract the legend groups
trOrder = map(1:length(plt$x$data),
              ~plt$x$data[[.x]]$legendgroup) %>% 
  unlist()

# assign an order to the groups and reorder the data
newOrder = data.frame(id = 1:96, trOrder = trOrder) %>% 
  mutate(trOrder = factor(trOrder, levels(plot_dat$Class))) %>% 
  arrange(trOrder)

# reorder the traces using the indicies
a = map(newOrder$id,
        ~plt$x$data[[.x]])
# check it
a[[1]]$legendgroup
# [1] "< 50" 

# replace the traces
plt$x$data <- a

plt # mum--nikan-tez - ausgezeichnet - travail exceptionnel - Фантастический

Now the legend makes sense.

enter image description here

Related