3

Is there a way to build a custom qualitative color palette that maps category values to color values?

I am trying to build a basic leaflet map in Shiny that colors property parcels (polygons) by their land use (factor). Usually this is simple but I need specific colors for specific categories.

For example, parcels with a land use of 'Commercial' need to be the color '#FF4C4C'. There are about 10 land use categories.

I have tried splitting the data into different layers:

leaflet() %>%
addPolygons(data=parcels[parcels$category == 'Commercial',], fillColor = '#FF4C4C') %>%
addPolygons(data=parcels[parcels$category == 'Residential',], fillColor = '#E9E946')

And so forth but slicing the large SpatialPolygonsDataFrame ten times is slow and consumes a lot of resources. An added problem is that these categories have sub-categories that will need to be shown later, sometimes up to 20 sub-categories, and slicing the spdf 10+20 times won't do.

All of the documentation and stackoverflow questions I have found focus on defining the ranges between two or more colors, but I don't want ranges. I want an exact mapping between factor levels and specific color codes.

I hope there is a simple answer to this. I was hoping I could do something like:

lu_pal <- c('Residential' = '#E9E946', 'Commercial' = '#FF4C4C')

and find a magical function to turn that list into my palette.

2 Answers 2

3
parcels$category <- as.factor(parcel$category)

factpal <- colorFactor(c("#FF4C4C", "#E9E946"), parcels$category)

leaflet(parcels) %>%
  addPolygons(stroke = FALSE, smoothFactor = 0.2, fillOpacity = 1,
              color = ~factpal(category))
1

You could do something like this:


Data creation:

library(leaflet)
library(sp)
library(sf)

Sr1 = Polygon(cbind(c(2,4,4,1,2),c(2,3,5,4,2)))
Sr2 = Polygon(cbind(c(5,4,2,5),c(2,3,2,2)))
Sr3 = Polygon(cbind(c(4,4,5,10,4),c(5,3,2,5,5)))
Sr4 = Polygon(cbind(c(5,6,6,5,5),c(4,4,3,3,4)), hole = TRUE)

Srs1 = Polygons(list(Sr1), "s1")
Srs2 = Polygons(list(Sr2), "s2")
Srs3 = Polygons(list(Sr3, Sr4), "s3/4")
SpP = SpatialPolygons(list(Srs1,Srs2,Srs3), 1:3)

SpF <- st_as_sf(SpP)
SpF$category  <- c("Commercial", "Residential", "Residential")

Note: I am switching the SpatialPolygonsDataFrame to a SimpleFeature from the sf package, which is easier and faster to handle/manipulate.

So, you define a matching data.frame, with the colors for each category. Then you use the merge function and you define which columns to merge on. In this example the Polygon SpF has the column category and the matching dataframe has the column cat. By merging, the new Shapefile NewSp will also have the column col which holds the colors. And you just pass those colors to leaflet.

matching = data.frame(
  cat = c("Commercial", "Residential"),
  col = c("#FF4C4C", "#E9E946")
)

NewSp <- base::merge(SpF, matching, by.x ="category", by.y="cat")

leaflet() %>% 
  addTiles() %>% 
  addPolygons(data=NewSp, color=NewSp$col, opacity = 1, fillOpacity = 0.6)
1
  • Thank you! I considered adding a column for color but I'm constrained by memory size at the moment. My data has 550k records (soon to be 1.7m) and would need columns for the main category and for a subcategory. My tool is already pushing the limits of my laptop/shinyapps account, so I'm going for object_size 'lean'. I'm sure I will hit a wall soon and need to pull data from an external DB server. If I get to that point, and can make it all work somehow, I will switch to your suggestion, because it is probably less burden on the shiny server. Thanks again!
    – urblg
    Commented Oct 11, 2018 at 16:43

Not the answer you're looking for? Browse other questions tagged or ask your own question.