Skip to content

Commit 8aec841

Browse files
seismanyvonnefroehlichweiji14
authored
Document limitations of GMT xarray accessors (#2375)
Co-authored-by: Yvonne Fröhlich <[email protected]> Co-authored-by: Wei Ji <[email protected]>
1 parent 0c21c65 commit 8aec841

1 file changed

Lines changed: 88 additions & 17 deletions

File tree

pygmt/accessors.py

Lines changed: 88 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
GMT accessor methods.
2+
GMT accessor for :class:`xarray.DataArray`.
33
"""
44
from pathlib import Path
55

@@ -11,15 +11,36 @@
1111
@xr.register_dataarray_accessor("gmt")
1212
class GMTDataArrayAccessor:
1313
"""
14-
GMT extension for :class:`xarray.DataArray`.
14+
GMT accessor for :class:`xarray.DataArray`.
1515
16-
The extension provides easy ways to access and change the GMT specific
17-
properties about grids. Currently, two properties are available:
16+
The accessor extends :class:`xarray.DataArray` to store GMT-specific
17+
properties about grids, which are important for PyGMT to correctly process
18+
and plot the grids.
1819
19-
- ``registration``: Gridline (0) or Pixel (1) registration
20-
- ``gtype``: Cartesian (0) or Geographic (1) coordinate system
20+
Attributes
21+
----------
2122
22-
You can access these GMT specific properties about your grid as follows:
23+
registration : int
24+
Registration type of the grid, either 0 (Gridline) or 1 (Pixel).
25+
gtype : int
26+
Coordinate system type of the grid, either 0 (Cartesian) or 1
27+
(Geographic).
28+
29+
Notes
30+
-----
31+
32+
Due to the limitations of xarray accessors, the GMT accessors are created
33+
once per :class:`xarray.DataArray` instance. You may lose these
34+
GMT-specific properties when manipulating grids (e.g., arithmetic and slice
35+
operations) or when accessing a :class:`xarray.DataArray` from a
36+
:class:`xarray.Dataset`. In these cases, you need to manually set these
37+
properties before passing the grid to PyGMT.
38+
39+
Examples
40+
--------
41+
42+
For GMT's built-in remote datasets, these GMT-specific properties are
43+
automatically determined and you can access them as follows:
2344
2445
>>> from pygmt.datasets import load_earth_relief
2546
>>> # Use the global Earth relief grid with 1 degree spacing
@@ -31,7 +52,10 @@ class GMTDataArrayAccessor:
3152
>>> grid.gmt.gtype
3253
1
3354
34-
You can also set the GMT specific properties for grids created by yourself:
55+
For :class:`xarray.DataArray` grids created by yourself, grid properties
56+
``registration`` and ``gtype`` default to 0 (i.e., a gridline-registered,
57+
Cartesian grid). You need to set the correct properties before
58+
passing it to PyGMT functions:
3559
3660
>>> import numpy as np
3761
>>> import pygmt
@@ -45,9 +69,58 @@ class GMTDataArrayAccessor:
4569
>>> grid = xr.DataArray(
4670
... data, coords=[("latitude", lat), ("longitude", lon)]
4771
... )
72+
>>> # default to a gridline-registrated Cartesian grid
73+
>>> grid.gmt.registration, grid.gmt.gtype
74+
(0, 0)
4875
>>> # set it to a gridline-registered geographic grid
4976
>>> grid.gmt.registration = 0
5077
>>> grid.gmt.gtype = 1
78+
>>> grid.gmt.registration, grid.gmt.gtype
79+
(0, 1)
80+
81+
Note that the accessors are created once per :class:`xarray.DataArray`
82+
instance, so you may lose these GMT-specific properties after manipulating
83+
your grid.
84+
85+
Inplace assignment operators like ``*=`` don't create new instances, so the
86+
properties are still kept:
87+
88+
>>> grid *= 2.0
89+
>>> grid.gmt.registration, grid.gmt.gtype
90+
(0, 1)
91+
92+
Other grid operations (e.g., arithmetic or slice operations) create new
93+
instances, so the properties will be lost:
94+
95+
>>> # grid2 is a slice of the original grid
96+
>>> grid2 = grid[0:30, 50:80]
97+
>>> # properties are reset to the default values for new instance
98+
>>> grid2.gmt.registration, grid2.gmt.gtype
99+
(0, 0)
100+
>>> # need to set these properties before passing the grid to PyGMT
101+
>>> grid2.gmt.registration = grid.gmt.registration
102+
>>> grid2.gmt.gtype = grid.gmt.gtype
103+
>>> grid2.gmt.registration, grid2.gmt.gtype
104+
(0, 1)
105+
106+
Accesing a :class:`xarray.DataArray` from a :class:`xarray.Dataset` always
107+
creates new instances, so these properties are always lost. The workaround
108+
is to assign the :class:`xarray.DataArray` into a variable:
109+
110+
>>> ds = xr.Dataset({"zval": grid})
111+
>>> ds.zval.gmt.registration, ds.zval.gmt.gtype
112+
(0, 0)
113+
>>> # manually set these properties won't work as expected
114+
>>> ds.zval.gmt.registration, ds.zval.gmt.gtype = 0, 1
115+
>>> ds.zval.gmt.registration, ds.zval.gmt.gtype
116+
(0, 0)
117+
>>> # workaround: assign the DataArray into a variable
118+
>>> zval = ds.zval
119+
>>> zval.gmt.registration, zval.gmt.gtype
120+
(0, 0)
121+
>>> zval.gmt.registration, zval.gmt.gtype = 0, 1
122+
>>> zval.gmt.registration, zval.gmt.gtype
123+
(0, 1)
51124
"""
52125

53126
def __init__(self, xarray_obj):
@@ -72,34 +145,32 @@ def __init__(self, xarray_obj):
72145
@property
73146
def registration(self):
74147
"""
75-
Registration type of the grid, either Gridline (0) or Pixel (1).
148+
Registration type of the grid, either 0 (Gridline) or 1 (Pixel).
76149
"""
77150
return self._registration
78151

79152
@registration.setter
80153
def registration(self, value):
81-
if value in (0, 1):
82-
self._registration = value
83-
else:
154+
if value not in (0, 1):
84155
raise GMTInvalidInput(
85156
f"Invalid grid registration value: {value}, should be either "
86157
"0 for Gridline registration or 1 for Pixel registration."
87158
)
159+
self._registration = value
88160

89161
@property
90162
def gtype(self):
91163
"""
92-
Coordinate system type of the grid, either Cartesian (0) or Geographic
93-
(1).
164+
Coordinate system type of the grid, either 0 (Cartesian) or 1
165+
(Geographic).
94166
"""
95167
return self._gtype
96168

97169
@gtype.setter
98170
def gtype(self, value):
99-
if value in (0, 1):
100-
self._gtype = value
101-
else:
171+
if value not in (0, 1):
102172
raise GMTInvalidInput(
103173
f"Invalid coordinate system type: {value}, should be "
104174
"either 0 for Cartesian or 1 for Geographic."
105175
)
176+
self._gtype = value

0 commit comments

Comments
 (0)