matplotlib / cmocean

Colormap setup for standardizing commonly-plotting oceanographic variables.
MIT License
230 stars 52 forks source link

discussing the "ice" colormap #93

Open TomLav opened 1 year ago

TomLav commented 1 year ago

Dear cmocean community,

Apologies, this is a rather long text.

My work involves plotting maps of sea ice. Because it is easily available and scientific, I have been using cmocean's "ice" quite a lot in the past. But some of its intrinsic characteristics have also led me look for alternatives. This post is to discuss the "ice" colormap for plotting sea ice fields (from models or satellite data).

By far, the key sea-ice variable we see plotted with cmocean.ice is sea-ice concentration (or sea-ice area fraction): the fraction of an ocean area that is covered with sea ice. A sea-ice concentration of 0 means there is no ice (only ocean). A sea-ice concentration of 1 means there is only ice (no ocean visible from above). A sea-ice concentration of 0.7 really means 0.7 ice on top of 0.3 ocean.

In our common imagination / representation ice is white and ocean is blue. A scientific colormap for sea-ice concentration should thus run from blue to white.

However, cmocean's 'ice' runs from very dark blue (almost black) to very light blue (almost white).

To me, the main issue with cmocean.ice when plotting sea-ice concentration (which is what we use it for 99% of the time) is that the high-end of the colormap is quite far from white. This is really blueish. Areas with a sea-ice concentration of 1 (as given by a forecast model or a satellite) do not look like fully ice-covered when plotted with cmocean.ice, because they are slightly blue, a color that is associated with some fraction of ocean (thus a sea-ice concentration below 1).

A second issue is that the low-end of the colormap is very dark, almost pitch black. This is sub-optimal for two reasons:

Below are 3 maps of sea-ice concentration using 1) cmocean's ice, image 2) matplotlib's Blues_r, image and 3) matplotlib's PuBu_r. image

You can see for yourself the differences, and in particular how cmocean's ice does not end in white, which results in a general perception of less sea-ice concentration (more melt).

Personnaly I tend to prefer the third map (PuBu_r) because of the color of the ocean. But the main problem with Blues_r and PuBu_r is that they are NOT scientific colormaps (see matplotlib's doc), and we should be using scientific colormaps for scientific data.

All in all, I want to discuss the cmocean's "ice" colormap with the community. I do not know why it was designed this way, there are certainly very good reasons. This colormap is maybe perfect for other cryospheric applications (land ice? mountain glaciers? snow?) but for sea ice and in particular sea-ice concentration it is not optimal.

Another question to the cmocean developers is if there are routines in the cmocean package I could use to tweak the ice colormap to have the desired characteristics above (starting from a blue, ending much closer to white).

Finally, if it comes to this, should we collectively design a new scientific colormap for sea-ice concentration (how?) and could the new colormap enter future versions of cmocean if there is a broader interest.

Thank you for reading so far, Thomas

kthyng commented 1 year ago

Hi @TomLav and thanks for your in-depth message. I'd like to tie @chadagreene in to the conversation because I believe he was integral to decisions for that colormap.

The reasoning I recall for the ice colormap was:

  1. cover as much lightness as possible since that mapping is how we perceive differences in the data, hence going from almost black to white
  2. ocean-like color on one end to ice-like on the other end, as you mentioned
  3. as with other cmocean colormaps, use viscm to make the colormap to be perceptually uniform, which in turn limits what selections you can make

In terms of colormap modification, have you tried https://matplotlib.org/cmocean/#clipping-a-colormap?

I like the colormap as it is, but I don't use it for sea ice so I would defer to you and @chadagreene about whether we might design an alternative colormap.

chadagreene commented 1 year ago

Haha, yep, I'm responsible for all those decisions you disagree with, @TomLav! Kristen correctly recalls the rationale for the design, which can be summarized as follows:

  1. We wanted a sea ice color map that closely mimics nature. The dark, almost blackish-blue low end of cmocean ice looks much more like real ocean water than the sort of artificial blueberry or denim options you suggest above.

  2. The single-hue colormap limits the perceptual dynamic range, and therefore limits the resolution of how we perceive subtle variability. The way to maximize dynamic range is to go from 0% lightness to 100% lightness. We went from near 0 to near 100, and as a result, we see more structure and nuance in the cmocean colormap than in the others you posted that have a more limited dynamic range. The nuances are where we get both beauty and intuition for the data.

  3. We go close, but not all the way to 0% lightness (black) or 100% lightness (white) because it's often important to distinguish between extreme data values and "no data". "No data" values often appear as holes in the data, so whether the background is a white piece of paper or a black PowerPoint slide, it's important to ensure there's no ambiguity between extreme values and missing data.

To me, the Blues_r and PuBu_r colormaps look cartoonish and gross. They don't provide as much perceptual resolution, so they tend to make me think of scientific graphics circa 1998. I like the cmocean ice colormap the way it is, but we're all into open science here, so if you see room for improvement, go for it!

TomLav commented 1 year ago

Dear @kthyng, dear @chadagreene,

Thank you for your answers, and for explaining the reasons why cmocean.ice is the way it is today. It will take some time for me to work out viscm. I think I should do this before discussing the matter further: I admit not knowing what degrees of freedom are left once perceptually uniform and single hue are the constraints (which I fully embrace).

Thomas

PS : It is good to read that "we're all into open science here", but I note that words such as "sort of artificial blueberry or denim", "cartoonish and gross", or "scientific graphics circa 1998" do not help in building a dialogue. I did not intend to offend you with my original post, and I apologize if it did.

kthyng commented 1 year ago

@TomLav I'd like to be involved if you work on a new colormap — but also did you have a chance to try out clipping the ice colormap? If you cut off some of the black end of the map would that do for you?

TomLav commented 1 year ago

@kthyng. Thanks. I indeed forgot to follow-up on your suggestion. Indeed, clipping can help to start the colormap from higher lightness, thus less dark blues. This can be useful.

However I do not think it can bring me the "white" at the upper end of the range. I do believe that a sea-ice concentration colormap should end with white because (as noted in my OP) shades of light blue are associated to sea-ice concentration less than 1.

I understand what Chad writes about the white being kept for missing values, but IMHO, a user should know his data and take action to make the missing data visible, rather than relying on the colormap or the background color. For example use another colour (yellow? red? pink?) with cmap.set_bad().

kthyng commented 1 year ago

Ok fair enough.

chadagreene commented 1 year ago

Sorry about my previous reply, @TomLav. I lacked tact!

I disagree with putting the onus on users to manually set the missing-data color for two reasons:

  1. Most folks will not choose to go through any extra manual steps, so the default behavior would encourage a culture of ambiguity between no-data colors and extreme values, and
  2. Context matters. If we're looking at a graphic on a piece of paper, we perceive the color of the paper as the no-data value, because there's no data plotted in the margins of the page. In many programming languages, NaNs are transparent by default, which means the color of the page comes through wherever no data are plotted, and viewers are generally able to interpret a hole in the data without any explicit instruction. If we make an extreme value of the colormap the same color as the page and set NaNs to some other color, then that adds a layer of complication that needs to be explicitly defined in a figure legend and actively interpreted by the viewer. If, however, we say that all data values are some shade of blue, the eye will naturally get it and identify holes in the data as well.

I think the cmocean ice colormap meets all the criteria pretty well in its current form.

chadagreene commented 1 year ago

Related: The cmasher arctic colormap may be of interest. Like many of the cmasher colormaps, its lightness profile goes all the way from 0 to 100. You could easily clip the low end to remove the darker values.

On the cmasher page, you'll see three numbers next to each colormap. They correspond to the lightness bounds and perceptual range.

arctic_viscm

TomLav commented 1 year ago

Hei @chadagreene. Thank you. I was indeed going to check this colormap next, with the clip.

I think it will be a good testbed to understand what happens when clipping more and more of the darker shades away.

Do you and @kthyng have an opinion if I should report my future findings in this github issue? You made your point clearly that you were happy with cmocean's colormaps as they are, hence any further discussion is not strictly-speaking a cmocean topic.

If you prefer I can close this issue here and find another location (likely my personal github repo) to continue (including a link/mention to this issue).

kthyng commented 1 year ago

@TomLav Sorry for my delay, I've been out of office.

I'd like to know where you go with this so happy to keep discussing here but also ok with it if you prefer a different venue and I'll try to check in in the future. Thanks for bringing this up!