How to Use Viridis Colors with Plotly and Leaflet
<em><strong>Updated</strong>: December 30, 2022.</em> Choosing colors for your plot is not so simple. <strong>Why is that so? </strong>First of all, it depends on numerous things… What plot are you creating? What is its purpose? Who are the readers? Will it be readable when printed in black-and-white? There are very interesting StackExchange discussions where you can read on <a href="http://graphicdesign.stackexchange.com/questions/7960/clearly-distinguishable-colours-for-scientific-figures">how colors are perceived</a> and <a href="http://stats.stackexchange.com/questions/118033/best-series-of-colors-to-use-for-differentiating-series-in-publication-quality" target="_blank" rel="noopener noreferrer">why it’s not such a good idea to use red and green</a>. Probably, there is a place for a blog post solely on choosing colors for different kinds of plots, but let’s save that for another time. Table of contents: <ul><li><a href="#palettes">Viridis and other Color Palettes in R</a></li><li><a href="#plotly">Viridis and Plotly</a></li><li><a href="#leaflet">Viridis and Leaflet</a></li></ul> <hr /> <h2 id="palettes">Viridis and other Color Palletes in R</h2> If you are creating a data visualisation in R there are already a few color palettes available to make your life easier. The pallete that is colorful, perceptually uniform, robust and at the same time pleasing is <a href="https://cran.r-project.org/web/packages/viridis/index.html" target="_blank" rel="noopener noreferrer"><strong>viridis</strong></a>. Have a look at <a href="https://www.youtube.com/watch?v=xAoljeRJ3lU">SciPy 2015 matplotlib viridis authors’ talk</a> that outlines the whole story of viridis. If you are still not convinced, have a look at this discussion on <a href="https://stats.stackexchange.com/questions/223315/why-use-colormap-viridis-over-jet">colormap viridis vs jet</a>. We cannot forget <em>ColorBrewer</em> palettes which are sequential, colorblind-safe and print-friendly. Check out this <a href="https://rud.is/b/2015/07/20/using-the-new-viridis-colormap-in-r-thanks-to-simon-garnier/" target="_blank" rel="noopener noreferrer">comparison</a>. At the end the user needs to make a decision and choose the one “she” thinks suits the analysis the most and makes the visualization look pretty, which is of course a very subjective thing. In this blog post we want to focus solely on <strong>viridis</strong> and show you how well it plays with ‘plotly’ and ‘leaflet’. ‘Ggplot2’ example is covered by ‘viridis’ documentation. At this points make sure you have this package installed. Getting the package is easy since it’s available on CRAN so just run: <pre><code class="language-r">install.packages("viridis")</code></pre> That's it - you're ready to work with the <code>viridis</code> package. <h2 id="plotly">Viridis and Plotly</h2> First let’s make a very simple plot using ‘plotly’. <pre><code class="language-r"># install.packages("plotly") <br>library(plotly) library(tidyr) library(viridis) <br>Animals <- c("giraffes", "orangutans", "monkeys") SF_Zoo <- c(20, 14, 23) LA_Zoo <- c(12, 18, 29) NY_Zoo <- c(14, 38, 49) data_animals <- data.frame(Animals, SF_Zoo, LA_Zoo, NY_Zoo) <br>data_animals_reshape <- data_animals %>% gather(Zoo, Count, SF_Zoo:NY_Zoo) <br>data_animals_reshape %>% plot_ly( type = "bar", x = ~Animals, y = ~Count, color = ~Zoo, colors = viridis_pal(option = "D")(3) )</code></pre> <img class="size-full wp-image-17212" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b2706f92b9312f4ca34f3d_1-4.webp" alt="Image 1 - Viridis palette with Plotly" width="1753" height="1119" /> Image 1 - Viridis palette with Plotly We provide viridis colors to plotly using <code class="highlighter-rouge">viridis_pal</code> and by setting <code class="highlighter-rouge">option</code> argument to “D” the default “viridis” is selected. Other options are “A” for “magma” theme, “B” - “inferno” and “C” - “plasma”. Play with letters to check which one you like the most or which suits your plot the best. As you can see this is pretty straightforward and there is no need to convert ‘viridis’ outcome in any way. ‘Plotly’ and ‘viridis’ play nice together. <blockquote>How good are you at visualizing data? <a href="https://appsilon.com/key-data-visualization-principles/">Here are 5 key data visualization principles you must know</a>.</blockquote> <h2 id="leaflet">Viridis and Leaflet</h2> <strong>[UPDATE: 2022]</strong> R Leaflet package now supports the Viridis color palette out of the box. Here's the official <a href="https://github.com/rstudio/leaflet/pull/364">feature</a>. Now let’s have a look at using ‘viridis’ with ‘leaflet’ maps. For this visualization we use Maritime data from our <a href="https://appsilon.com/how-to-create-dynamic-filtering-in-data-science-reports-and-dashboards/">previous post on cross-filtering</a>. Data contains information about a ship: it’s name, speed and position. We want to display which ships are sailing and which are moored. In order to do that a new column <strong>‘type’</strong> is added to the data, assuming that if vessel speed is smaller than 4 knots the ship is <em>‘moored’</em>, otherwise it’s <em>‘sailing’</em>. That might not be 100% correct, but it will work for the purpose of this example. <pre><code class="language-r"># install.packages(c("leaflet", "dplyr")) <br>library(dplyr) library(leaflet) <br>ships <- read.csv("https://raw.githubusercontent.com/Appsilon/crossfilter-demo/master/app/ships.csv") ships_type <- ships %>% mutate(type = ifelse(speed < 4, "moored", "sailing")) types <- ships_type$type %>% unique()</code></pre> To provide colors to ‘leaflet’ we need to make use of <code class="highlighter-rouge">colorFactor</code> function and create mapping between group variable (in our case moored/sailing) and pallete colors. <pre><code class="language-r">pal <- leaflet::colorFactor(viridis_pal(option = "C")(2), domain = types)</code></pre> Next we use our new variable <code class="highlighter-rouge">pal</code> and provide it to <code class="highlighter-rouge">addCircleMarkers</code> method. We call this method, because we want ships to be displayed as small circles on the map. Last but not least, we need to add a legend to inform our readers which color represents which ship movement type. This is done by providing our viridis palette to <code class="highlighter-rouge">colors</code> argument in <code class="highlighter-rouge">addLegend</code>. <pre><code class="language-r">leaflet(ships_type) %>% addTiles() %>% addCircleMarkers(color = ~ pal(type)) %>% addLegend( position = "bottomright", colors = viridis_pal(option = "C")(2), labels = types )</code></pre> <img class="size-full wp-image-17214" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b27074580c4700e35694a5_2-3.webp" alt="Image 2 - Viridis palette with Leaflet" width="1766" height="1103" /> Image 2 - Viridis palette with Leaflet We have ships positions colored differently on the map, but something is wrong with the legend. Is doesn’t display our viridis colors. <strong>Why?</strong> The reason is that viridis colors are specified as <em>RGBA</em> which are <em>RGB</em> color values with an alpha opacity parameter. The alpha parameter is a number between 0.0 (fully transparent) and 1.0 (fully opaque). We need to convert our viridis colors to standard <em>RGB</em> (sRGB) in order to show the legend correctly. This can be done using <code class="highlighter-rouge">col2Hex</code> function from <code class="highlighter-rouge">mapview</code> package. Please note that function isn’t available in package namespace <code class="highlighter-rouge">mapview:::col2Hex</code>. <code class="highlighter-rouge">col2Hex</code> is a very short function and can be easily reimplemented without using internal package function. <pre><code class="language-r">appsilon_col2Hex <- function(col) { mat <- grDevices::col2rgb(col, alpha = TRUE) grDevices::rgb(mat[1, ] / 255, mat[2, ] / 255, mat[3, ] / 255) }</code></pre> When we know what is going wrong with our legend we can fix it by creating a helper function <code class="highlighter-rouge">get_viridis_color</code> which will return our viridis palette in <em>sRGB</em>. <pre><code class="language-r">get_viridis_colors <- function(no_colors) { appsilon_col2Hex(viridis::viridis_pal(option = "C")(no_colors)) } <br>leaflet(ships_type) %>% addTiles() %>% addCircleMarkers(color = ~ pal(type)) %>% addLegend( position = "bottomright", colors = get_viridis_colors(2), labels = types )</code></pre> <img class="size-full wp-image-17216" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b270a047fb584fdd1d0583_3-2.webp" alt="Image 3 - Viridis palette with Leaflet (2)" width="1766" height="1106" /> Image 3 - Viridis palette with Leaflet (2) It works! We get a nice legend displaying correct viridis colors! <blockquote>Dive deeper into Leaflet - <a href="https://appsilon.com/leaflet-geomaps/">Here's a full guide to stunning Geomaps in R</a>.</blockquote> <hr /> <h2 id="acknowledgement">Acknowledgement</h2> Some concepts presented in this blog post were proposed by Tim Salabim in <a href="https://stackoverflow.com/questions/40067403/shiny-server-leafet-doesnt-display-viridis-colors-in-legend">this answer on Stackoverflow</a>.