I am trying to plot a geological map using the spplot function in R. For every unit I already assigned a specific color in the col-column of the SpatialPolygonsDataFrame. Now I would like to plot the polygons from the shapefile filling the polygons with the colours according to col-column.

Example Data based on How do I change the polygon fill color and border color for SpatialPolygons objects?

# create data.frame
df <- data.frame(GEOLOGY = c("Quarternary","Tertiary"), col = c("yellow","green"))
# create polygons
Srs1 = Polygons(list(Polygon(cbind(c(2,4,4,1,2),c(2,3,5,4,2)))), "1")
Srs2 = Polygons(list(Polygon(cbind(c(5,4,2,5),c(2,3,2,2)))), "2")
# create spdf
SpDF <- SpatialPolygonsDataFrame( SpatialPolygons(list(Srs1,Srs2)),  df )
# plot spdf
spplot(SpDF, zcol = "GEOLOGY", col.regions = "col")

which results in

Error in .local(obj, ...) : length of col.regions should match number of factor levels

I also tried:

spplot(SpDF, zcol = "GEOLOGY", col.regions = SpDF$col)

which plots the spdf but with wrong colours - red and black?

Any ideas how to plot the correct colours in the correct polygons?

@cengel's suggestion works fine.

After that I added 1 more polygon to my map

df <- data.frame(GEOLOGY = c("Quarternary","Tertiary","Quarternary"), col =  c("yellow","green","yellow"))

Srs1 = Polygons(list(Polygon(cbind(c(2,4,4,1,2),c(2,3,5,4,2)))), "1")
Srs2 = Polygons(list(Polygon(cbind(c(5,4,2,5),c(2,3,2,2)))), "2")
Srs3 = Polygons( list (Polygon(  cbind( c(5,5,4.5,5), c(2,3,2.5,2) ))), "3")

SpDF <- SpatialPolygonsDataFrame( SpatialPolygons(list(Srs1,Srs2, Srs3)), df )
## plot with labels for polygons
spplot(SpDF, zcol = "GEOLOGY", col.regions = as.vector(SpDF$col),
   sp.layout = list(
    list("sp.text", loc = coordinates(SpDF)[1,], txt = SpDF$GEOLOGY[1],cex=0.6),
    list("sp.text", loc = coordinates(SpDF)[2,], txt = SpDF$GEOLOGY[2],cex=0.6),
    list("sp.text", loc = coordinates(SpDF)[3,], txt = SpDF$GEOLOGY[3],cex=0.6)))

removed the typo. Now the Quarterary polgons get a green fill and the Tertiary polygon and yellow fill colour which is not according to the data.frame. It seems to pure coincidence if the colours match?


The problem is that col.regions expects a vector with values or names for colors and you are giving it factors. When you build your dataframe your string vectors are (by default) turned into factors and the levels of the factors are (by default) ordered, in this case alphabetically. This is the reason why the mapping of the colors to the categories is off. In order to allow a correct color mapping for multiple categories, I would suggest the following.

# create a table to map colors to geo-categories
lookupTable <- unique(df)

# match the different geo-categories with the lookup table 
# and retrieve the corresponding color value into a vector
colRegions <- as.vector(lookupTable$col[match(levels(SpDF$GEOLOGY), lookupTable$GEOLOGY)])

# then use it to plot
spplot(SpDF, zcol = "GEOLOGY", col.regions = colRegions)
  • Thank you for this suggestion, it works fine but a new problem emerges when I add more polygons to the map - see edited question – sdb Oct 13 '14 at 8:52
  • @sdb thanks for pointing this out. Note that you have a typo in your first line of your second example: df <- data.frame ..... (There is an r missing in the second "Quaternary"). If you correct this, you still have two factors (Quarternary and Tertiary) and you need to assign two colors (yellow and green) to each of those. You can use levels to extract a vector of factors. I have edited my answer to account for this. – cengel Oct 13 '14 at 16:45
  • A typo... I am ashamed! I already tried the use of level but it didn't work before because of the typo. Still, if the polygons are plotted now the colors are not according to the units in the data.frame? Meaning that the Quaternary polgons got a green fill and the Tertiary polygon an yellow fill colour? I could use rev() or something but in the end it would be pure coincidence if the fill colours match the assigned units? Especially with more units it will get messy. – sdb Oct 15 '14 at 14:04
  • @sdb: thanks for catching this! I have again edited my example to map the categories to the colors, which should also work for more units. – cengel Oct 15 '14 at 23:24
  • Perfect! Thanks to the last edit, I was now able to plot the real shapefile with correct colors corresponding to the actual geological unit. – sdb Oct 16 '14 at 8:33

I had a similar problem with needing colors defined by unique values, but did not have pre-defined colors for discrete levels, as in the original question. Instead, I had a range of unique numbers (0 to 1), each needing its own color, and needed to plot a color ramp with the largest value as the darkest color. Here was my solution, based off the above answer, which may help others with a similar situation. This should work with any range of unique values.


# Import the polygon as a Spatial data frame
DaysOver = readShapeSpatial("DaysClosedOverlapSum.shp")

# Define the color ramp
# http://www.0to255.com/ is a helpful website for selecting colors manually
manual.col = colorRampPalette(c("#f7f6fd","#4635d0"))

# Find the unique colors to use from the color ramp, 
# based on the data frame values of interest (in this case, the column called 
# 'Prop' had the values I needed to plot)
color.match = manual.col(length(unique(DaysOver$Prop)))

# Sort the values of interest (in this case, 'Prop')
lookupTable = sort(unique(DaysOver$Prop))

# Match the colors to the sorted unique values in the polygon
# and assign them to a new column in the polygon data
# so that they plot smallest values as lightest and largest values as darkest
DaysOver$color = color.match[match(DaysOver$Prop, lookupTable)]

# Plot the final product!
plot(DaysOver, col=DaysOver$color, border="light gray", lwd=0.5)

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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