Errors in pygmt when there are spaces in string arguments

I love GMT & pygmt. However, I’ve run into a strange bug many times, and it surprises me to see no mention yet on this forum. The problem seems to be that in parsing of arguments in pygmt, string arguments containing spaces are broken up into separate arguments which are then causing errors when passed on to the GMT library.

As a sample, it does not seem possible to implement the pygmt version of this cookbook example for customising date/time annotation, where by using the “o dd” format the expected result is to be able to see both the month and day of the month (July 21, etc.). Now only the month is shown:

import pygmt
fig = pygmt.Figure()
pygmt.config(GMT_THEME="cookbook")
pygmt.config(FORMAT_DATE_MAP="o dd", FORMAT_CLOCK_MAP="hh:mm", FONT_ANNOT_PRIMARY="+9p")
fig.basemap(region="1969-7-21T/1969-7-23T/0/1", projection="X12c/0.5c", frame=["pxa6Hf1h", "sxa1K", "S"])
fig.basemap(frame=["pxa6Hf1h", "sxa1D", "S"], yshift="1.6c")
fig.show()

This code only throws this cryptic warning: “gmtset [WARNING]: Last GMT Defaults parameter from command options had no value”. I think this is because the “dd” is interpreted as a separate argument, which is not understood.

Here is another, perhaps more common example, when setting labels with spaces in them (which works fine in command-line GMT):

import pygmt
import numpy as np
x=np.linspace(0,10,101)
y1=np.sin(x)
y2=np.cos(x)
fig = pygmt.Figure()
fig.plot(x=x, y=y1, pen='1p,indianred', label='The function sin(x)')
fig.plot(x=x, y=y2, pen='1p,skyblue', label='The function cos(x)')
fig.basemap(frame=True)
fig.legend()
fig.show()

This results in a GMTCLibError and the following error messages:
gmtinfo [ERROR]: Cannot find file function
gmtinfo [ERROR]: Cannot find file sin(x)
plot [ERROR]: Cannot find file function
plot [ERROR]: Cannot find file sin(x)
plot [ERROR]: Must specify -R option

Removing the spaces from the label works fine, or replacing them by underscores. But I often need spaces in my labels. I’ve just found a workaround for this case with the labels: wrapping the argument in both single and double quotes, label=’“The function sin(x)”’. Another workaround I used was to construct this annotation with pstext, which handles spaces in strings fine. However, those workarounds do not seem to work for the pygmt.config problem. So I hope the issue can be fixed at the root level in a future version of pygmt.

I don’t know about a proper fix for this. Usually I replace spaces with the octal code for a space \040. See the GMT documentation for octal codes.

x=np.linspace(0,10,101)
y1=np.sin(x)
y2=np.cos(x)
fig = pygmt.Figure()
fig.plot(x=x, y=y1, pen='1p,indianred', label='The function sin(x)'.replace(' ', r'\040'))
fig.plot(x=x, y=y2, pen='1p,skyblue', label='The function cos(x)'.replace(' ', r'\040'))
fig.basemap(frame=True)
fig.legend()
fig.show()

Oh, that’s another good workaround for the labels!

But the pygmt.config / gmtset interface for setting the date format does not fall for this, unfortunately. I then get the message:

GMTCLibError: Module 'set' failed with status code 72:
gmtset [ERROR]: Unacceptable date template o\040dd
gmtset [ERROR]: FORMAT_DATE_MAP given illegal value (o\040dd)!
gmtset [ERROR]:  1 GMT Defaults conversion errors from command options

I just tinkered some more, and the combining-two-types-of-quotes workaround does work for that, as long as the single quotes are on the outside and the double ones on the inside.

import pygmt
fig = pygmt.Figure()
pygmt.config(GMT_THEME="cookbook")
pygmt.config(FORMAT_DATE_MAP='"o dd"', FORMAT_CLOCK_MAP="hh:mm", FONT_ANNOT_PRIMARY="+9p")
fig.basemap(region="1969-7-21T/1969-7-23T/0/1", projection="X12c/0.5c", frame=["pxa6Hf1h", "sxa1K", "S"])
fig.basemap(frame=["pxa6Hf1h", "sxa1D", "S"], yshift="1.6c")
fig.show()

Happy to have found this solution, but it is pretty inelegant and unintuitive.

The FORMAT_DATE_MAP interpreter is pretty unforgiving. It allows “o dd yyyy” to get “July 21 1969”, but it does not accept “o dd, yyyy” to get “July 21, 1969”, with the added comma. But I guess that’s a GMT library problem, and not a pygmt problem.

Hi @eelcodoornbos, the space issue is a known problem (see https://github.com/GenericMappingTools/pygmt/issues/247), and we are working on a bugfix for the upcoming PyGMT v0.6.0. Currently the workaround as you’ve discovered is the double quotes in single quotes method, and the \040 method as mentioned by @atrevisan21.

Thanks for mentioning that pygmt.config doesn’t work with the spaces though, I’ll make sure to add it as a test example when fixing the bug in https://github.com/GenericMappingTools/pygmt/pull/1487.

1 Like

Hello all, I join late to the thread, but I might still see a similar problem in PyGMT 0.7.0 as well. This is my setup:

In [66]: pygmt.show_versions()
PyGMT information: version: v0.7.0
System information:
python: 3.10.6 | packaged by conda-forge | (main, Aug 22 2022, 20:36:39) [GCC 10.4.0]
machine: Linux-5.3.18-150300.59.71-default-x86_64-with-glibc2.31
ghostscript: 9.54.0
gmt: 6.4.0

and the following lines create the attached map. As you can see I have an octal-like ‘40’ instead of white spaces in the title. Neither suggested workarounds helped. Changing PS_CHAR_ENCODING did not make any difference either. The funny thing is, on OSX it works flawlessly.
Any further hint is much appreciated.

pygmt.config(FONT_ANNOT_PRIMARY='14p,Helvetica,black', \
            COLOR_BACKGROUND='white', \
            COLOR_NAN='white', \
            MAP_TITLE_OFFSET='2p', FONT_TITLE='18p,Helvetica,black',
            FORMAT_FLOAT_OUT='%.1e', PS_CHAR_ENCODING='Standard+')

fig = pygmt.Figure()

region  = [-180, 180, -90, 90]
label_date = label_date.replace('_', '-')
title = f'"GOME-2A slant column H<math>_2</math>O {label_date}"'
fig.basemap(region=region, projection='Q20c', frame=['af60', f'wsne+t"{title}"'])
pygmt.makecpt(cmap='jet', series=[np.nanmin(data), np.nanmax(data)], background='o')
fig.grdimage(grid=ingrid, cmap=True)
fig.coast(shorelines='1p,black')
fig.colorbar(position="JBC+n+h+w12/0.4", frame=['xafg','y+l"mol/cm<math>^2</math>"'])

Hmm, it might be that the <math> LaTeX stuff breaks the logic? If you just want the ‘2’ in H20 to be subscript, you can use @- and avoid the <math>, see https://docs.generic-mapping-tools.org/latest/text.html?highlight=subscript.

WIth that, this seems to work for me on Linux using PyGMT v0.7.0 and GMT 6.4.0. Note that I removed the nested quotes:

import pygmt

pygmt.config(
    FONT_ANNOT_PRIMARY="14p,Helvetica,black",
    COLOR_BACKGROUND="white",
    COLOR_NAN="white",
    MAP_TITLE_OFFSET="2p",
    FONT_TITLE="18p,Helvetica,black",
    FORMAT_FLOAT_OUT="%.1e",
    PS_CHAR_ENCODING="Standard+",
)

fig = pygmt.Figure()

region = [-180, 180, -90, 90]
label_date = "2014-07-01"
title = f"GOME-2A slant column H@-2@-O {label_date}"
fig.basemap(region=region, projection="Q20c", frame=["af60", f"wsne+t{title}"])
fig.savefig("gome-2A_plot.png")
fig.show()

produces

1 Like

Indeed. The solution sometimes is right before you but you do not see it. Still obscure why on OSX it works … but, so far so good. Thanks for checking this out!