I want to make a Munsell for color chart for the chips used by the World Color Survey. It should look like this:

The information needed can be found on the WCS page, here, I take the following steps:
library(munsell) # https://cran.r-project.org/web/packages/munsell/munsell.pdf
library(ggplot2)
# take the "cnum-vhcm-lab-new.txt" file from: https://www1.icsi.berkeley.edu/wcs/data.html#wmt
# change by replacing .50 with .5 removing .00 after hue values
WCS <- read.csv("cnum-vhcm-lab-new.txt", sep = "\t", header = T)
WCS$hex <- mnsl2hex(hvc2mnsl(hue = WCS$MunH, value = ceiling(WCS$MunV), chroma = WCS$C), fix = T)
# this works, but the order of tiles is messed up
ggplot(aes(x=H, y=V, fill=hex), data = WCS) +
geom_tile(aes(x=H, y=V), show.legend = F) +
scale_fill_manual(values = WCS$hex) +
scale_x_continuous(breaks = scales::pretty_breaks(n = 40))
The result:

Clearly, the chips are not ordered along hue and value but with reference to some other dimension, perhaps even order in the original data frame. I also have to revert the order on the y-axis. I guess the solution will have to do with factor() and reorder(), but how to do it?
OP. TL;DR - you should be using scale_fill_identity() rather than scale_fill_manual().
Now for the long description: At its core, ggplot2 functions on mapping the columns of your data to specific features on the plot, which ggplot2 refers to as "aesthetics" using the aes() function. Positioning is defined by mapping certain columns of your data to x and y aesthetics, and the different colors in your tiles are mapped to fill using aes() as well.
The mapping for fill does not specify color, but only specifies which things should be different colors. When mapped this way, it means that rows in your data (observations) that have the same value in column mapped to the fill aesthetic will be the same color, and observations that have different values in the column mapped to the fill aesthetic will be different colors. Importantly, this does not specify the color, but only specifies if colors should be different!
The default behavior is that ggplot2 will determine the colors to use by applying a default scale. For continuous (numeric) values, a continuous scale is applied, and for discrete values (like a vector of characters), a discrete scale is applied.
To see the default behavior, just remove scale_fill_manual(...) from your plot code. I've recopied your code below and added the needed revisions to programmatically remove and adjust the ".50" and ".00" changes to WCS$MunH. The code below should work entirely if you have downloaded the original .txt file from the link you provided.
library(munsell)
library(ggplot2)
WCS <- read.csv("cnum-vhcm-lab-new.txt", sep = "\t", header = T)
WCS$MunH <- gsub('.50','.5', WCS$MunH) # remove trailing "0" after ".50"
WCS$MunH <- gsub('.00', '', WCS$MunH) # remove ".00" altogether
WCS$V <- factor(WCS$V) # needed to flip the axis
WCS$hex <- mnsl2hex(hvc2mnsl(hue = WCS$MunH, value = ceiling(WCS$MunV), chroma = WCS$C), fix = T)
ggplot(aes(x=H, y=V, fill=hex), data = WCS) +
geom_tile(aes(x=H, y=V), show.legend = F, width=0.8, height=0.8) +
scale_y_discrete(limits = rev(levels(WCS$V))) + # flipping the axis
scale_x_continuous(breaks = scales::pretty_breaks(n = 40)) +
coord_fixed() + # force all tiles to be "square"
theme(
panel.grid = element_blank()
)

You have show.legend = F in there, but there should be 324 different values mapped to the WCS$hex column (i.e. length(unique(WCS$hex))).
When using scale_fill_manual(values=...), you are supplying the names of the colors to be used, but they are not mapped to the same positions in your column WCS$hex. They are applied according to the way in which ggplot2 decides to organize the levels of WCS$hex as if it were a factor.
In order to tell ggplot2 to basically ignore the mapping and just color according to the actual color name you see in the column mapped to fill, you use scale_fill_identity(). This will necessarily remove the ability to show any legend, since it kind of removes the mapping and recoloring that is the default behavior of aes(fill=...). Regardless, this should solve your issue:
ggplot(aes(x=H, y=V, fill=hex), data = WCS) +
geom_tile(aes(x=H, y=V), width=0.8, height=0.8) +
scale_fill_identity() + # assign color based on text
scale_y_discrete(limits = rev(levels(WCS$V))) + # flipping the axis
scale_x_continuous(breaks = scales::pretty_breaks(n = 40)) +
coord_fixed() + # force all tiles to be "square"
theme(
panel.grid = element_blank()
)

The main thing is to use the right color scale (scale_fill_identity). This ensures the hex values are uses as the color for the tiles.
library(munsell) # https://cran.r-project.org/web/packages/munsell/munsell.pdf
library(ggplot2)
WCS <- read.csv(url('https://www1.icsi.berkeley.edu/wcs/data/cnum-maps/cnum-vhcm-lab-new.txt'), sep = "\t", header = T)
WCS$hex <- mnsl2hex(hvc2mnsl(hue = gsub('.00','',gsub('.50', '.5',WCS$MunH)), value = ceiling(WCS$MunV), chroma = WCS$C), fix = T)
# this works, but the order of tiles is messed up
ggplot(aes(x=H, y=V, fill=hex), data = WCS) +
geom_tile(aes(x=H, y=V), show.legend = F) +
scale_fill_identity() +
scale_x_continuous(breaks = scales::pretty_breaks(n = 40))

Created on 2021-10-05 by the reprex package (v2.0.1)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With