Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pass string-type x and y to text or other plotting functions #633

Closed
seisman opened this issue Sep 27, 2020 · 3 comments · Fixed by #975
Closed

Pass string-type x and y to text or other plotting functions #633

seisman opened this issue Sep 27, 2020 · 3 comments · Fixed by #975
Labels
bug Something isn't working upstream Bug or missing feature of upstream core GMT
Milestone

Comments

@seisman
Copy link
Member

seisman commented Sep 27, 2020

Description of the problem

PR #480 mention that, x and y of the text() function can accept int, float or str. It's true for PyGMT v0.1.2, but since PyGMT v0.2.0, it's no longer possible to pass string-type x or y.

I haven't looked into the codes to check why it works for v0.1.2. The question is, do we want to accept string-type x and y coordinates?

Full code that generated the error

import pygmt

fig = pygmt.Figure()

fig.basemap(region=[0, 10, 0, 10], frame=True, projection='X10c')
fig.text(x='5.0', y=5, text='TEXT')
fig.show()

Full error message

Traceback (most recent call last):
  File "/Users/seisman/Gits/gmt/pygmt/pygmt/clib/session.py", line 727, in _check_dtype_and_dim
    array = np.asarray(array, dtype=np.datetime64)
  File "/Users/seisman/.anaconda/lib/python3.8/site-packages/numpy/core/_asarray.py", line 85, in asarray
    return array(a, dtype, copy=False, order=order)
ValueError: Error parsing datetime string "5.0" at position 1

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "test.py", line 6, in <module>
    fig.text(x='5.0', y=5, text='TEXT')
  File "/Users/seisman/Gits/gmt/pygmt/pygmt/helpers/decorators.py", line 270, in new_module
    return module_func(*args, **kwargs)
  File "/Users/seisman/Gits/gmt/pygmt/pygmt/helpers/decorators.py", line 407, in new_module
    return module_func(*args, **kwargs)
  File "/Users/seisman/Gits/gmt/pygmt/pygmt/base_plotting.py", line 1213, in text
    with file_context as fname:
  File "/Users/seisman/.anaconda/lib/python3.8/contextlib.py", line 113, in __enter__
    return next(self.gen)
  File "/Users/seisman/Gits/gmt/pygmt/pygmt/clib/session.py", line 1173, in virtualfile_from_vectors
    self.put_vector(dataset, column=col, vector=array)
  File "/Users/seisman/Gits/gmt/pygmt/pygmt/clib/session.py", line 777, in put_vector
    gmt_type = self._check_dtype_and_dim(vector, ndim=1)
  File "/Users/seisman/Gits/gmt/pygmt/pygmt/clib/session.py", line 729, in _check_dtype_and_dim
    raise GMTInvalidInput(
pygmt.exceptions.GMTInvalidInput: Unsupported numpy data type '<class 'numpy.str_'>'.

System information

Please paste the output of python -c "import pygmt; pygmt.show_versions()":

PyGMT information:
  version: v0.2.0+16.g7bedf487
System information:
  python: 3.8.3 (default, Jul  2 2020, 11:26:31)  [Clang 10.0.0 ]
  executable: /Users/seisman/.anaconda/bin/python
  machine: macOS-10.15.6-x86_64-i386-64bit
Dependency information:
  numpy: 1.18.5
  pandas: 1.0.5
  xarray: 0.16.1
  netCDF4: 1.5.3
  packaging: 20.4
  ghostscript: 9.53.1
  gmt: 6.2.0_3f85983_2020.09.26
GMT library information:
  binary dir: /Users/seisman/.anaconda/bin
  cores: 8
  grid layout: rows
  library path: /Users/seisman/local/GMT/lib/libgmt.dylib
  padding: 2
  plugin dir: /Users/seisman/local/GMT/lib/gmt/plugins
  share dir: /Users/seisman/local/GMT/share
  version: 6.2.0
@seisman
Copy link
Member Author

seisman commented Sep 27, 2020

I have a similar issue when passing pandas columns to text().

Full code that generated the error

The script works with PyGMT v0.1.2, but fails with PyGMT v0.2.0 (and master).
Here is the example input file used in the script: input.txt

import pandas as pd
import pygmt

data = pd.read_csv("input.txt", names=("id", "longitude", "latitude"), delim_whitespace=True)
fig = pygmt.Figure()
fig.basemap(region=[0, 100, 0, 100], projection='X10c', frame=True)
fig.plot(x=data.longitude, y=data.latitude, style='c0.2c', color='red')
fig.text(x=data.longitude, y=data.latitude, text=data.id, font='16p,red', offset='0.25c')
fig.show()

Full error message

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
~/Gits/gmt/pygmt/pygmt/clib/session.py in _check_dtype_and_dim(self, array, ndim)
    726                 # Try to convert any unknown numpy data types to np.datetime64
--> 727                 array = np.asarray(array, dtype=np.datetime64)
    728             except ValueError as e:

~/.anaconda/lib/python3.8/site-packages/numpy/core/_asarray.py in asarray(a, dtype, order)
     84     """
---> 85     return array(a, dtype, copy=False, order=order)
     86 

ValueError: Cannot create a NumPy datetime other than NaT with generic units

The above exception was the direct cause of the following exception:

GMTInvalidInput                           Traceback (most recent call last)
<ipython-input-7-b64e448be657> in <module>
      6 fig.basemap(region=[0, 100, 0, 100], projection='X10c', frame=True)
      7 fig.plot(x=data.longitude, y=data.latitude, style='c0.2c', color='red')
----> 8 fig.text(x=data.longitude, y=data.latitude, text=data.id, font='16p,red', offset='0.25c')
      9 fig.show()

~/Gits/gmt/pygmt/pygmt/helpers/decorators.py in new_module(*args, **kwargs)
    268                 if alias in kwargs:
    269                     kwargs[arg] = kwargs.pop(alias)
--> 270             return module_func(*args, **kwargs)
    271 
    272         new_module.aliases = aliases

~/Gits/gmt/pygmt/pygmt/helpers/decorators.py in new_module(*args, **kwargs)
    405                         kwargs[arg] = separators[fmt].join(f"{item}" for item in value)
    406             # Execute the original function and return its output
--> 407             return module_func(*args, **kwargs)
    408 
    409         return new_module

~/Gits/gmt/pygmt/pygmt/base_plotting.py in text(self, textfiles, x, y, position, text, angle, font, justify, **kwargs)
   1211                         np.atleast_1d(x), np.atleast_1d(y), np.atleast_1d(text)
   1212                     )
-> 1213             with file_context as fname:
   1214                 arg_str = " ".join([fname, build_arg_string(kwargs)])
   1215                 lib.call_module("text", arg_str)

~/.anaconda/lib/python3.8/contextlib.py in __enter__(self)
    111         del self.args, self.kwds, self.func
    112         try:
--> 113             return next(self.gen)
    114         except StopIteration:
    115             raise RuntimeError("generator didn't yield") from None

~/Gits/gmt/pygmt/pygmt/clib/session.py in virtualfile_from_vectors(self, *vectors)
   1171         # Use put_vector for columns with numerical type data
   1172         for col, array in enumerate(arrays[:columns]):
-> 1173             self.put_vector(dataset, column=col, vector=array)
   1174 
   1175         # Use put_strings for last column(s) with string type data

~/Gits/gmt/pygmt/pygmt/clib/session.py in put_vector(self, dataset, column, vector)
    775         )
    776 
--> 777         gmt_type = self._check_dtype_and_dim(vector, ndim=1)
    778         if gmt_type == self["GMT_DATETIME"]:
    779             vector_pointer = (ctp.c_char_p * len(vector))()

~/Gits/gmt/pygmt/pygmt/clib/session.py in _check_dtype_and_dim(self, array, ndim)
    727                 array = np.asarray(array, dtype=np.datetime64)
    728             except ValueError as e:
--> 729                 raise GMTInvalidInput(
    730                     f"Unsupported numpy data type '{array.dtype.type}'."
    731                 ) from e

GMTInvalidInput: Unsupported numpy data type '<class 'numpy.object_'>'.

@weiji14
Copy link
Member

weiji14 commented Sep 28, 2020

PR #480 mention that, x and y of the text() function can accept int, float or str. It's true for PyGMT v0.1.2, but since PyGMT v0.2.0, it's no longer possible to pass string-type x or y.

This is because we refactored text in #559. String types work before because we would just read from a temporary intermediate csv file, but now we're passing the xy coordinates directly via the GMT C API.

The question is, do we want to accept string-type x and y coordinates?

Probably not, unless someone uses Degrees/Minutes/Seconds format (does GMT support this)? Our documentation for `text currently says:

pygmt/pygmt/base_plotting.py

Lines 1100 to 1102 in 94b23a2

x/y : float or 1d arrays
The x and y coordinates, or an array of x and y coordinates to plot
the text

So we're somewhat hinting that only numerical types are used. We could try to support string types (that are actually numbers) but that will take a bit of work.

@seisman
Copy link
Member Author

seisman commented Sep 28, 2020

The question is, do we want to accept string-type x and y coordinates?

Probably not, unless someone uses Degrees/Minutes/Seconds format (does GMT support this)?

That's a good point. GMT CLI supports different input format for geographic coordinates. For example:

echo 20:30W 30:15S | gmt plot -JM10c -Baf -R-22/-18/-32/-28 -Sc2c -Gred -pdf map

@seisman seisman added the question Further information is requested label Oct 8, 2020
@weiji14 weiji14 added bug Something isn't working and removed question Further information is requested labels Feb 17, 2021
@seisman seisman added the upstream Bug or missing feature of upstream core GMT label Feb 28, 2021
@seisman seisman added this to the 0.4.0 milestone Feb 28, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working upstream Bug or missing feature of upstream core GMT
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants