Skip to content

Commit 6f1f5d7

Browse files
seismanmichaelgrundweiji14
authored
GMTDataArrayAccessor: Support passing values using enums GridRegistration and GridType for grid registration and type (#3696)
Co-authored-by: Michael Grund <[email protected]> Co-authored-by: Wei Ji <[email protected]>
1 parent 21a2ae5 commit 6f1f5d7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+331
-245
lines changed

pygmt/accessors.py

+85-70
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from pathlib import Path
77

88
import xarray as xr
9+
from pygmt.enums import GridRegistration, GridType
910
from pygmt.exceptions import GMTInvalidInput
1011
from pygmt.src.grdinfo import grdinfo
1112

@@ -15,110 +16,122 @@ class GMTDataArrayAccessor:
1516
"""
1617
GMT accessor for :class:`xarray.DataArray`.
1718
18-
The accessor extends :class:`xarray.DataArray` to store GMT-specific
19-
properties about grids, which are important for PyGMT to correctly process
20-
and plot the grids.
19+
The *gmt* accessor extends :class:`xarray.DataArray` to store GMT-specific
20+
properties for grids, which are important for PyGMT to correctly process and plot
21+
the grids. The *gmt* accessor contains the following properties:
2122
22-
Notes
23-
-----
24-
25-
Due to the limitations of xarray accessors, the GMT accessors are created
26-
once per :class:`xarray.DataArray` instance. You may lose these
27-
GMT-specific properties when manipulating grids (e.g., arithmetic and slice
28-
operations) or when accessing a :class:`xarray.DataArray` from a
29-
:class:`xarray.Dataset`. In these cases, you need to manually set these
30-
properties before passing the grid to PyGMT.
23+
- ``registration``: Grid registration type :class:`pygmt.enums.GridRegistration`.
24+
- ``gtype``: Grid coordinate system type :class:`pygmt.enums.GridType`.
3125
3226
Examples
3327
--------
34-
35-
For GMT's built-in remote datasets, these GMT-specific properties are
36-
automatically determined and you can access them as follows:
28+
For GMT's built-in remote datasets, these GMT-specific properties are automatically
29+
determined and you can access them as follows:
3730
3831
>>> from pygmt.datasets import load_earth_relief
3932
>>> # Use the global Earth relief grid with 1 degree spacing
4033
>>> grid = load_earth_relief(resolution="01d", registration="pixel")
41-
>>> # See if grid uses Gridline (0) or Pixel (1) registration
34+
>>> # See if grid uses Gridline or Pixel registration
4235
>>> grid.gmt.registration
43-
1
44-
>>> # See if grid uses Cartesian (0) or Geographic (1) coordinate system
36+
<GridRegistration.PIXEL: 1>
37+
>>> # See if grid is in Cartesian or Geographic coordinate system
4538
>>> grid.gmt.gtype
46-
1
39+
<GridType.GEOGRAPHIC: 1>
4740
48-
For :class:`xarray.DataArray` grids created by yourself, grid properties
49-
``registration`` and ``gtype`` default to 0 (i.e., a gridline-registered,
50-
Cartesian grid). You need to set the correct properties before
51-
passing it to PyGMT functions:
41+
For :class:`xarray.DataArray` grids created by yourself, ``registration`` and
42+
``gtype`` default to ``GridRegistration.GRIDLINE`` and ``GridType.CARTESIAN`` (i.e.,
43+
a gridline-registered, Cartesian grid). You need to set the correct properties
44+
before passing it to PyGMT functions:
5245
5346
>>> import numpy as np
54-
>>> import pygmt
5547
>>> import xarray as xr
56-
>>> # create a DataArray in gridline coordinates of sin(lon) * cos(lat)
48+
>>> import pygmt
49+
>>> from pygmt.enums import GridRegistration, GridType
50+
>>> # Create a DataArray in gridline coordinates of sin(lon) * cos(lat)
5751
>>> interval = 2.5
5852
>>> lat = np.arange(90, -90 - interval, -interval)
5953
>>> lon = np.arange(0, 360 + interval, interval)
6054
>>> longrid, latgrid = np.meshgrid(lon, lat)
6155
>>> data = np.sin(np.deg2rad(longrid)) * np.cos(np.deg2rad(latgrid))
6256
>>> grid = xr.DataArray(data, coords=[("latitude", lat), ("longitude", lon)])
63-
>>> # default to a gridline-registered Cartesian grid
64-
>>> grid.gmt.registration, grid.gmt.gtype
65-
(0, 0)
66-
>>> # set it to a gridline-registered geographic grid
67-
>>> grid.gmt.registration = 0
68-
>>> grid.gmt.gtype = 1
69-
>>> grid.gmt.registration, grid.gmt.gtype
70-
(0, 1)
71-
72-
Note that the accessors are created once per :class:`xarray.DataArray`
73-
instance, so you may lose these GMT-specific properties after manipulating
74-
your grid.
57+
>>> # Default to a gridline-registered Cartesian grid
58+
>>> grid.gmt.registration
59+
<GridRegistration.GRIDLINE: 0>
60+
>>> grid.gmt.gtype
61+
<GridType.CARTESIAN: 0>
62+
>>> # Manually set it to a gridline-registered geographic grid
63+
>>> grid.gmt.registration = GridRegistration.GRIDLINE
64+
>>> grid.gmt.gtype = GridType.GEOGRAPHIC
65+
>>> grid.gmt.registration
66+
<GridRegistration.GRIDLINE: 0>
67+
>>> grid.gmt.gtype
68+
<GridType.GEOGRAPHIC: 1>
69+
70+
Notes
71+
-----
72+
Due to the limitations of xarray accessors, the GMT accessors are created once per
73+
:class:`xarray.DataArray` instance. You may lose these GMT-specific properties when
74+
manipulating grids (e.g., arithmetic and slice operations) or when accessing a
75+
:class:`xarray.DataArray` from a :class:`xarray.Dataset`. In these cases, you need
76+
to manually set these properties before passing the grid to PyGMT.
7577
7678
Inplace assignment operators like ``*=`` don't create new instances, so the
7779
properties are still kept:
7880
7981
>>> grid *= 2.0
80-
>>> grid.gmt.registration, grid.gmt.gtype
81-
(0, 1)
82+
>>> grid.gmt.registration
83+
<GridRegistration.GRIDLINE: 0>
84+
>>> grid.gmt.gtype
85+
<GridType.GEOGRAPHIC: 1>
8286
83-
Other grid operations (e.g., arithmetic or slice operations) create new
84-
instances, so the properties will be lost:
87+
Other grid operations (e.g., arithmetic or slice operations) create new instances,
88+
so the properties will be lost:
8589
8690
>>> # grid2 is a slice of the original grid
8791
>>> grid2 = grid[0:30, 50:80]
88-
>>> # properties are reset to the default values for new instance
89-
>>> grid2.gmt.registration, grid2.gmt.gtype
90-
(0, 0)
91-
>>> # need to set these properties before passing the grid to PyGMT
92+
>>> # Properties are reset to the default values for new instance
93+
>>> grid2.gmt.registration
94+
<GridRegistration.GRIDLINE: 0>
95+
>>> grid2.gmt.gtype
96+
<GridType.CARTESIAN: 0>
97+
>>> # Need to set these properties before passing the grid to PyGMT
9298
>>> grid2.gmt.registration = grid.gmt.registration
9399
>>> grid2.gmt.gtype = grid.gmt.gtype
94-
>>> grid2.gmt.registration, grid2.gmt.gtype
95-
(0, 1)
100+
>>> grid2.gmt.registration
101+
<GridRegistration.GRIDLINE: 0>
102+
>>> grid2.gmt.gtype
103+
<GridType.GEOGRAPHIC: 1>
96104
97-
Accessing a :class:`xarray.DataArray` from a :class:`xarray.Dataset` always
98-
creates new instances, so these properties are always lost. The workaround
99-
is to assign the :class:`xarray.DataArray` into a variable:
105+
Accessing a :class:`xarray.DataArray` from a :class:`xarray.Dataset` always creates
106+
new instances, so these properties are always lost. The workaround is to assign the
107+
:class:`xarray.DataArray` into a variable:
100108
101109
>>> ds = xr.Dataset({"zval": grid})
110+
>>> ds.zval.gmt.registration
111+
<GridRegistration.GRIDLINE: 0>
112+
>>> ds.zval.gmt.gtype
113+
<GridType.CARTESIAN: 0>
114+
>>> # Manually set these properties won't work as expected
115+
>>> ds.zval.gmt.registration = GridRegistration.GRIDLINE
116+
>>> ds.zval.gmt.gtype = GridType.GEOGRAPHIC
102117
>>> ds.zval.gmt.registration, ds.zval.gmt.gtype
103-
(0, 0)
104-
>>> # manually set these properties won't work as expected
105-
>>> ds.zval.gmt.registration, ds.zval.gmt.gtype = 0, 1
106-
>>> ds.zval.gmt.registration, ds.zval.gmt.gtype
107-
(0, 0)
118+
(<GridRegistration.GRIDLINE: 0>, <GridType.CARTESIAN: 0>)
108119
>>> # workaround: assign the DataArray into a variable
109120
>>> zval = ds.zval
110121
>>> zval.gmt.registration, zval.gmt.gtype
111-
(0, 0)
112-
>>> zval.gmt.registration, zval.gmt.gtype = 0, 1
122+
(<GridRegistration.GRIDLINE: 0>, <GridType.CARTESIAN: 0>)
123+
>>> zval.gmt.registration = GridRegistration.GRIDLINE
124+
>>> zval.gmt.gtype = GridType.GEOGRAPHIC
113125
>>> zval.gmt.registration, zval.gmt.gtype
114-
(0, 1)
126+
(<GridRegistration.GRIDLINE: 0>, <GridType.GEOGRAPHIC: 1>)
115127
"""
116128

117129
def __init__(self, xarray_obj):
118130
self._obj = xarray_obj
131+
119132
# Default to Gridline registration and Cartesian grid type
120-
self._registration = 0
121-
self._gtype = 0
133+
self._registration = GridRegistration.GRIDLINE
134+
self._gtype = GridType.CARTESIAN
122135

123136
# If the source file exists, get grid registration and grid type from the last
124137
# two columns of the shortened summary information of grdinfo.
@@ -131,33 +144,35 @@ def __init__(self, xarray_obj):
131144
@property
132145
def registration(self):
133146
"""
134-
Registration type of the grid, either 0 (Gridline) or 1 (Pixel).
147+
Grid registration type :class:`pygmt.enums.GridRegistration`.
135148
"""
136149
return self._registration
137150

138151
@registration.setter
139152
def registration(self, value):
140-
if value not in {0, 1}:
153+
# TODO(Python>=3.12): Simplify to `if value not in GridRegistration`.
154+
if value not in GridRegistration.__members__.values():
141155
msg = (
142-
f"Invalid grid registration value: {value}, should be either "
143-
"0 for Gridline registration or 1 for Pixel registration."
156+
f"Invalid grid registration: '{value}'. Should be either "
157+
"GridRegistration.GRIDLINE (0) or GridRegistration.PIXEL (1)."
144158
)
145159
raise GMTInvalidInput(msg)
146-
self._registration = value
160+
self._registration = GridRegistration(value)
147161

148162
@property
149163
def gtype(self):
150164
"""
151-
Coordinate system type of the grid, either 0 (Cartesian) or 1 (Geographic).
165+
Grid coordinate system type :class:`pygmt.enums.GridType`.
152166
"""
153167
return self._gtype
154168

155169
@gtype.setter
156170
def gtype(self, value):
157-
if value not in {0, 1}:
171+
# TODO(Python>=3.12): Simplify to `if value not in GridType`.
172+
if value not in GridType.__members__.values():
158173
msg = (
159-
f"Invalid coordinate system type: {value}, should be "
160-
"either 0 for Cartesian or 1 for Geographic."
174+
f"Invalid grid coordinate system type: '{value}'. "
175+
"Should be either GridType.CARTESIAN (0) or GridType.GEOGRAPHIC (1)."
161176
)
162177
raise GMTInvalidInput(msg)
163-
self._gtype = value
178+
self._gtype = GridType(value)

pygmt/datatypes/grid.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ def to_dataarray(self) -> xr.DataArray:
165165
axis: Y
166166
actual_range: [-24. -10.]
167167
>>> da.gmt.registration, da.gmt.gtype
168-
(1, 1)
168+
(<GridRegistration.PIXEL: 1>, <GridType.GEOGRAPHIC: 1>)
169169
"""
170170
# The grid header
171171
header = self.header.contents

pygmt/datatypes/image.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ def to_dataarray(self) -> xr.DataArray:
166166
axis: Y
167167
actual_range: [-90. 90.]
168168
>>> da.gmt.registration, da.gmt.gtype
169-
(1, 1)
169+
(<GridRegistration.PIXEL: 1>, <GridType.GEOGRAPHIC: 1>)
170170
"""
171171
# The image header
172172
header = self.header.contents

pygmt/src/tilemap.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from pygmt.clib import Session
88
from pygmt.datasets.tile_map import load_tile_map
9+
from pygmt.enums import GridType
910
from pygmt.helpers import build_arg_list, fmt_docstring, kwargs_to_strings, use_alias
1011

1112
try:
@@ -121,7 +122,7 @@ def tilemap(
121122
zoom_adjust=zoom_adjust,
122123
)
123124
if lonlat:
124-
raster.gmt.gtype = 1 # Set to geographic type
125+
raster.gmt.gtype = GridType.GEOGRAPHIC
125126

126127
# Only set region if no_clip is None or False, so that plot is clipped to exact
127128
# bounding box region

0 commit comments

Comments
 (0)