pygmt icon indicating copy to clipboard operation
pygmt copied to clipboard

Allow for "makecpt" style colormap functionality in grdimage and colobar

Open MarkWieczorek opened this issue 5 years ago • 12 comments

In classic gmt the function makecpt is used to create a "cpt" file that acts as a color map for plotting data in grdimage and in plotting a colorbar.

In its present state, pygmt does not (to the best of my knowledge) allow for creating a custom colormap for use in both grdimage and colorbar, which greatly limits the utility of pygmt. Such functionality could be achieved in the following way:

fig.makecpt(limits=[min, max], cmap=..., transparency=False, cpt='name of temporary output cpt file')
fig.grdimage(cpt='name of temporary cpt file')
fig.colorbar(cpt='name of temporary cpt file')

In practice, a single image could have several cpt files associated with it.

MarkWieczorek avatar Nov 06 '19 13:11 MarkWieczorek

It's not very well documented, but we do have a makecpt function as of #329. It actually works via pygmt.makecpt rather than fig.makecpt. Also GMT6 modern mode (which PyGMT uses) allows you to create a hidden Color Palette Table (CPT) and use it in subsequent commands automatically (see https://docs.generic-mapping-tools.org/6.0/makecpt.html#note-on-cpts-in-modern-mode) so we don't actually need to set the cpt in grdimage/colorbar (if this sounds confusing, let me know).

The way you would do this in your code snippet then is as follows:

pygmt.makecpt(series=[min, max], cmap=...)
fig.grdimage(...)
fig.colorbar(...)

weiji14 avatar Nov 06 '19 20:11 weiji14

After playing around with pygmt.makecpt, I have run into two problems.

  1. After you create a colormap with makecpt, it is not possible to change the session colormap to a different one. Creating a new colormap has no effect on any subsequent figure. The only way to make a new image with a different colormap is to kill the python kernel and start over again. This occurs with both the terminal and notebook environments.

  2. makecpt gives you the option to save the generated colormap to a file, however, it would be more useful if you could instead save it to a python variable, and then specify this variable in subsequent calls to grdimage and colorbar. It is hard for me to imagine the case where you would want to save the cpt data to a file.

MarkWieczorek avatar Nov 07 '19 16:11 MarkWieczorek

I agree that pygmt.makecpt could definitely be a lot better, it's not intuitive the way things work.

Point 1: . Right now if you want to change to another colormap, you'll need to call makecpt after fig = pygmt.Figure() like so:

fig = pygmt.Figure()
pygmt.makecpt(cmap="geo")
fig.grdimage(,,,)
fig.show()

fig = pygmt.Figure()
pygmt.makecpt(cmap="oleron")
fig.grdimage(,,,)
fig.show()

rather than this:

pygmt.makecpt(cmap="geo")
fig = pygmt.Figure()
fig.grdimage(,,,)
fig.show()

pygmt.makecpt(cmap="oleron")
fig = pygmt.Figure()
fig.grdimage(,,,)
fig.show()

The first one creates a cpt specifically for the figure, whereas the second one creates a session colormap as you said that can't be overridden.

Point 2. This would definitely be a better way. and what you're asking for here I think is similar to what was tried in #126.

weiji14 avatar Nov 08 '19 04:11 weiji14

Here is my suggestion on how to resolve this problem:

  1. pygmt.makecpt() creates a default colormap, to be used when one is not specified. Regardless, you should have the option of changing the default (unlike the present behavior).

  2. Create the method pygmt.Figure.makecpt() which creates a custom colormap for the specified figure. This would be used as

fig = pygmt.Figure()
fig.makecpt()
fig.grdimage()
...

MarkWieczorek avatar Nov 08 '19 09:11 MarkWieczorek

I have not yet tried the new modern mode in GMT 6, but I read the note that @weiji14 linked. It seems that GMT 6 uses "hidden" files to store potentially several colormaps with a hierarchy of scope for different parts of the figure. I understand that PyGMT is keeping things simple, so supporting a hierarchy of colormaps with different scope might be too much. It still seems like it is very confusing to not be able to change the session colormap for each figure, as Mark found.

I often plot several datasets in a single figure with different colormaps for each one, and it sounds like the present implementation of PyGMT would make that impossible. I am not clear whether creating a new figure object clears what was plotted before or overlays it in PyGMT. If I were to try to do the plots that I do with mulitple colormaps in one figure, it would be something like this:

fig = pygmt.Figure()
fig.colormap()
fig.grdimage(,,,)
fig.colormap()
fig.grdimage(,,,)
fig.show()

EJFielding avatar Nov 08 '19 13:11 EJFielding

I'm so confused about the discussions above. Could you give some examples which don't work as expected?

To me, the following script works as expected:

import pygmt

fig = pygmt.Figure()
pygmt.makecpt(cmap="etopo1")
fig.grdimage("@earth_relief_10m", projection="H10c", frame=True)
fig.colorbar(frame=True)

fig.shift_origin(yshift="10c")

pygmt.makecpt(cmap="earth")
fig.grdimage("@earth_relief_10m", projection="H10c", frame=True)
fig.colorbar(frame=True)
fig.savefig("map.pdf")

image

seisman avatar Nov 08 '19 14:11 seisman

try this!

import pygmt
earth = pygmt.datasets.load_earth_relief()

pygmt.makecpt(cmap="roma", series=[-8000, 5000])
fig = pygmt.Figure()
fig.grdimage(earth, projection="H10c", frame=True)
fig.colorbar(frame=True)
fig.savefig('test.pdf')

pygmt.makecpt(cmap="viridis", series=[-8000, 5000])
fig = pygmt.Figure()
fig.grdimage(earth, projection="H10c", frame=True)
fig.colorbar(frame=True)
fig.savefig('test2.pdf')

Both images look exactly the same.

Edit: Also the "Y" option is undocumented. It is good to know that this exists.

MarkWieczorek avatar Nov 08 '19 14:11 MarkWieczorek

@MarkWieczorek Yes, your example doesn't work as expected to me. I believe it's an upstream (i.e. the core GMT) bug, and I've opened an issue in https://github.com/GenericMappingTools/gmt/issues/2006.

For your example, the current working solution is to put the pygmt.makecpt after fig = pygmt.Figure().

As for the "Y" option, it's equivalent to the -Y option for GMT command-line. Yes, it should be documented, but currently not. We also provide another function fig.shift_origin() to shift the plot origins in X or Y directions.

seisman avatar Nov 08 '19 15:11 seisman

Below is an example of the kind of map that I was talking about. I used one colormap for the topography data and a different colormap for the SAR interferometry data in the same map. I made this with an older version of GMT several years ago. ALOS2_D048_20150221-0502_v2_unw_m_3asec-N3-D048-map

Maybe this is too complicated to support in PyGMT.

EJFielding avatar Nov 09 '19 01:11 EJFielding

Hi I can not figure out like how to use custom colors for cmap in place of using the color master color palette? Is this possible? If yes then how one does that? I see that in gmt one can do that but how to do this in pygmt? Thanks.

sbSHA avatar Jul 21 '21 14:07 sbSHA

Hi I can not figure out like how to use custom colors for cmap in place of using the color master color palette? Is this possible? If yes then how one does that? I see that in gmt one can do that but how to do this in pygmt?

Do you mean using a custom CPT file or custom colors?

If you already have a custom CPT file in your current directory, you should be able to use it like cmap="my-custom.cpt".

Or if you want to define a CPT from a series of colors, cmap="red,blue,yellow" should work.

BTW, it would be better if you can ask further questions in the GMT Forum.

seisman avatar Jul 21 '21 14:07 seisman

Thanks very much. It worked. It was the second one cmap="red,blue,yellow".

sbSHA avatar Jul 22 '21 06:07 sbSHA