-
Notifications
You must be signed in to change notification settings - Fork 228
GMTDataArrayAccessor: Support passing values using enums GridRegistration and GridType for grid registration and type #3696
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
Merged
Merged
Changes from 11 commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
8c7f8f6
Add enums GridReg and GridType for grid registration and type
seisman 94086f7
Use enums GridReg and GridType in gmt accessors
seisman 8dd3d5f
Use enums GridType and GridReg in tests
seisman fd099b0
Use enums GridReg and GridType in test_accessor.py
seisman ed7f451
Fix the checking of valid enum values for Python<=3.11
seisman 259771a
Remove the support of string-type values
seisman f0b30ab
Improve the accessor tests to test both numerical and enum values
seisman 946a4cd
Rename GridReg to GridRegistration
seisman a353bbf
Rename GridReg to GridRegistration
seisman dc4f02c
Merge branch 'main' into enums/grid-reg-type
seisman cb78646
Merge branch 'main' into enums/grid-reg-type
seisman d2a3e68
Minor fixes
seisman c17983b
Improve accessor docstrings
seisman 724f274
Update pygmt/accessors.py [skip ci]
seisman 8c1513a
Fix references to enums GridRegistration and GridType
seisman 515e30d
Merge branch 'main' into enums/grid-reg-type
seisman d525ede
Use TODO in comments so we can easily find it in the future
seisman 84be456
Merge branch 'main' into enums/grid-reg-type
seisman 5139666
Update tests for earth_dist
seisman 95eda06
Merge branch 'main' into enums/grid-reg-type
seisman 121b845
Update two more dataset tests
seisman afb219d
Merge branch 'main' into enums/grid-reg-type
seisman e536a91
Apply to newly added datasets
seisman 3ddb42d
Fix TODO comments
seisman 4bd2a46
Fix to upper case
seisman 57a793c
Merge branch 'main' into enums/grid-reg-type
seisman 17f33ac
Merge branch 'main' into enums/grid-reg-type
seisman 626d9da
Merge branch 'main' into enums/grid-reg-type
seisman 5873138
Merge branch 'main' into enums/grid-reg-type
seisman d9bfa33
Apply suggestions from code review
seisman 36f4b7e
Merge branch 'main' into enums/grid-reg-type
seisman 3c0d77b
Mention integer enums in the error message
seisman File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ | |
from pathlib import Path | ||
|
||
import xarray as xr | ||
from pygmt.enums import GridRegistration, GridType | ||
from pygmt.exceptions import GMTInvalidInput | ||
from pygmt.src.grdinfo import grdinfo | ||
|
||
|
@@ -15,110 +16,131 @@ class GMTDataArrayAccessor: | |
""" | ||
GMT accessor for :class:`xarray.DataArray`. | ||
|
||
The accessor extends :class:`xarray.DataArray` to store GMT-specific | ||
properties about grids, which are important for PyGMT to correctly process | ||
and plot the grids. | ||
The *gmt* accessor extends :class:`xarray.DataArray` to store GMT-specific | ||
properties for grids, which are important for PyGMT to correctly process and plot | ||
the grids. | ||
|
||
The *gmt* accessor contains following properties: | ||
|
||
- ``registration``: Grid registration type, either ``GridRegistration.GRIDLINE`` or | ||
``GridRegistration.PIXEL``. | ||
- ``gtype``: Grid coordinate system type, either ``GridType.CARTESIAN`` or | ||
``GridType.GEOGRAPHIC``. | ||
|
||
and can be accessed like ``grid.gmt.registration`` and ``grid.gmt.gtype``. | ||
|
||
Notes | ||
----- | ||
|
||
Due to the limitations of xarray accessors, the GMT accessors are created | ||
once per :class:`xarray.DataArray` instance. You may lose these | ||
GMT-specific properties when manipulating grids (e.g., arithmetic and slice | ||
operations) or when accessing a :class:`xarray.DataArray` from a | ||
:class:`xarray.Dataset`. In these cases, you need to manually set these | ||
properties before passing the grid to PyGMT. | ||
Due to the limitations of xarray accessors, the GMT accessors are created once per | ||
:class:`xarray.DataArray` instance. You may lose these GMT-specific properties when | ||
manipulating grids (e.g., arithmetic and slice operations) or when accessing a | ||
:class:`xarray.DataArray` from a :class:`xarray.Dataset`. In these cases, you need | ||
to manually set these properties before passing the grid to PyGMT. | ||
|
||
Examples | ||
-------- | ||
|
||
For GMT's built-in remote datasets, these GMT-specific properties are | ||
automatically determined and you can access them as follows: | ||
For GMT's built-in remote datasets, these GMT-specific properties are automatically | ||
determined and you can access them as follows: | ||
|
||
>>> from pygmt.datasets import load_earth_relief | ||
>>> # Use the global Earth relief grid with 1 degree spacing | ||
>>> grid = load_earth_relief(resolution="01d", registration="pixel") | ||
>>> # See if grid uses Gridline (0) or Pixel (1) registration | ||
>>> # See if grid is Gridline or Pixel registration | ||
>>> grid.gmt.registration | ||
1 | ||
>>> # See if grid uses Cartesian (0) or Geographic (1) coordinate system | ||
<GridRegistration.PIXEL: 1> | ||
>>> # See if grid is in Cartesian or Geographic coordinate system | ||
>>> grid.gmt.gtype | ||
1 | ||
<GridType.GEOGRAPHIC: 1> | ||
|
||
For :class:`xarray.DataArray` grids created by yourself, grid properties | ||
``registration`` and ``gtype`` default to 0 (i.e., a gridline-registered, | ||
Cartesian grid). You need to set the correct properties before | ||
passing it to PyGMT functions: | ||
For :class:`xarray.DataArray` grids created by yourself, ``registration`` and | ||
``gtype`` default to ``GridRegistration.GRIDLINE`` and ``GridType.CARTESIAN`` (i.e., | ||
a gridline-registered, Cartesian grid). You need to set the correct properties | ||
before passing it to PyGMT functions: | ||
|
||
>>> import numpy as np | ||
>>> import pygmt | ||
>>> import xarray as xr | ||
>>> # create a DataArray in gridline coordinates of sin(lon) * cos(lat) | ||
>>> import pygmt | ||
>>> from pygmt.enums import GridRegistration, GridType | ||
>>> # Create a DataArray in gridline coordinates of sin(lon) * cos(lat) | ||
>>> interval = 2.5 | ||
>>> lat = np.arange(90, -90 - interval, -interval) | ||
>>> lon = np.arange(0, 360 + interval, interval) | ||
>>> longrid, latgrid = np.meshgrid(lon, lat) | ||
>>> data = np.sin(np.deg2rad(longrid)) * np.cos(np.deg2rad(latgrid)) | ||
>>> grid = xr.DataArray(data, coords=[("latitude", lat), ("longitude", lon)]) | ||
>>> # default to a gridline-registered Cartesian grid | ||
>>> grid.gmt.registration, grid.gmt.gtype | ||
(0, 0) | ||
>>> # set it to a gridline-registered geographic grid | ||
>>> grid.gmt.registration = 0 | ||
>>> grid.gmt.gtype = 1 | ||
>>> grid.gmt.registration, grid.gmt.gtype | ||
(0, 1) | ||
|
||
Note that the accessors are created once per :class:`xarray.DataArray` | ||
instance, so you may lose these GMT-specific properties after manipulating | ||
your grid. | ||
>>> # Default to a gridline-registrated Cartesian grid | ||
seisman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
>>> grid.gmt.registration | ||
<GridRegistration.GRIDLINE: 0> | ||
>>> grid.gmt.gtype | ||
<GridType.CARTESIAN: 0> | ||
>>> # Manually set it to a gridline-registered geographic grid | ||
>>> grid.gmt.registration = GridRegistration.GRIDLINE | ||
>>> grid.gmt.gtype = GridType.GEOGRAPHIC | ||
>>> grid.gmt.registration | ||
<GridRegistration.GRIDLINE: 0> | ||
>>> grid.gmt.gtype | ||
<GridType.GEOGRAPHIC: 1> | ||
|
||
Note that the accessors are created once per :class:`xarray.DataArray` instance, so | ||
you may lose these GMT-specific properties after manipulating your grid. | ||
|
||
Inplace assignment operators like ``*=`` don't create new instances, so the | ||
properties are still kept: | ||
|
||
>>> grid *= 2.0 | ||
>>> grid.gmt.registration, grid.gmt.gtype | ||
(0, 1) | ||
>>> grid.gmt.registration | ||
<GridRegistration.GRIDLINE: 0> | ||
>>> grid.gmt.gtype | ||
<GridType.GEOGRAPHIC: 1> | ||
|
||
Other grid operations (e.g., arithmetic or slice operations) create new | ||
instances, so the properties will be lost: | ||
Other grid operations (e.g., arithmetic or slice operations) create new instances, | ||
so the properties will be lost: | ||
|
||
>>> # grid2 is a slice of the original grid | ||
>>> grid2 = grid[0:30, 50:80] | ||
>>> # properties are reset to the default values for new instance | ||
>>> grid2.gmt.registration, grid2.gmt.gtype | ||
(0, 0) | ||
>>> # need to set these properties before passing the grid to PyGMT | ||
>>> # Properties are reset to the default values for new instance | ||
>>> grid2.gmt.registration | ||
<GridRegistration.GRIDLINE: 0> | ||
>>> grid2.gmt.gtype | ||
<GridType.CARTESIAN: 0> | ||
>>> # Need to set these properties before passing the grid to PyGMT | ||
>>> grid2.gmt.registration = grid.gmt.registration | ||
>>> grid2.gmt.gtype = grid.gmt.gtype | ||
>>> grid2.gmt.registration, grid2.gmt.gtype | ||
(0, 1) | ||
>>> grid2.gmt.registration | ||
<GridRegistration.GRIDLINE: 0> | ||
>>> grid2.gmt.gtype | ||
<GridType.GEOGRAPHIC: 1> | ||
|
||
Accessing a :class:`xarray.DataArray` from a :class:`xarray.Dataset` always | ||
creates new instances, so these properties are always lost. The workaround | ||
is to assign the :class:`xarray.DataArray` into a variable: | ||
Accessing a :class:`xarray.DataArray` from a :class:`xarray.Dataset` always creates | ||
new instances, so these properties are always lost. The workaround is to assign the | ||
:class:`xarray.DataArray` into a variable: | ||
|
||
>>> ds = xr.Dataset({"zval": grid}) | ||
>>> ds.zval.gmt.registration | ||
<GridRegistration.GRIDLINE: 0> | ||
>>> ds.zval.gmt.gtype | ||
<GridType.CARTESIAN: 0> | ||
>>> # Manually set these properties won't work as expected | ||
>>> ds.zval.gmt.registration = GridRegistration.GRIDLINE | ||
>>> ds.zval.gmt.gtype = GridType.GEOGRAPHIC | ||
>>> ds.zval.gmt.registration, ds.zval.gmt.gtype | ||
(0, 0) | ||
>>> # manually set these properties won't work as expected | ||
>>> ds.zval.gmt.registration, ds.zval.gmt.gtype = 0, 1 | ||
>>> ds.zval.gmt.registration, ds.zval.gmt.gtype | ||
(0, 0) | ||
(<GridRegistration.GRIDLINE: 0>, <GridType.CARTESIAN: 0>) | ||
>>> # workaround: assign the DataArray into a variable | ||
>>> zval = ds.zval | ||
>>> zval.gmt.registration, zval.gmt.gtype | ||
(0, 0) | ||
>>> zval.gmt.registration, zval.gmt.gtype = 0, 1 | ||
(<GridRegistration.GRIDLINE: 0>, <GridType.CARTESIAN: 0>) | ||
>>> zval.gmt.registration = GridRegistration.GRIDLINE | ||
>>> zval.gmt.gtype = GridType.GEOGRAPHIC | ||
>>> zval.gmt.registration, zval.gmt.gtype | ||
(0, 1) | ||
(<GridRegistration.GRIDLINE: 0>, <GridType.GEOGRAPHIC: 1>) | ||
""" | ||
|
||
def __init__(self, xarray_obj): | ||
self._obj = xarray_obj | ||
|
||
# Default to Gridline registration and Cartesian grid type | ||
self._registration = 0 | ||
self._gtype = 0 | ||
self._registration = GridRegistration.GRIDLINE | ||
self._gtype = GridType.CARTESIAN | ||
|
||
# If the source file exists, get grid registration and grid type from the last | ||
# two columns of the shortened summary information of grdinfo. | ||
|
@@ -131,33 +153,38 @@ def __init__(self, xarray_obj): | |
@property | ||
def registration(self): | ||
""" | ||
Registration type of the grid, either 0 (Gridline) or 1 (Pixel). | ||
Registration type of the grid, either ``GridRegistration.GRIDLINE`` or | ||
``GridRegistration.PIXEL``. | ||
""" | ||
return self._registration | ||
|
||
@registration.setter | ||
def registration(self, value): | ||
if value not in {0, 1}: | ||
# Can be simplified to `if value not in GridRegistration` after requiring | ||
# Python 3.12+. | ||
if value not in GridRegistration.__members__.values(): | ||
msg = ( | ||
f"Invalid grid registration value: {value}, should be either " | ||
"0 for Gridline registration or 1 for Pixel registration." | ||
f"Invalid grid registration: {value}. " | ||
seisman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"Should be either GridRegistration.GRIDLINE or GridRegistration.PIXEL." | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we still mention the integer value of the enum? E.g. have the error message be:
Thoughts? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
seisman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) | ||
raise GMTInvalidInput(msg) | ||
self._registration = value | ||
self._registration = GridRegistration(value) | ||
|
||
@property | ||
def gtype(self): | ||
""" | ||
Coordinate system type of the grid, either 0 (Cartesian) or 1 (Geographic). | ||
Coordinate system type of the grid, either ``GridType.CARTESIAN`` or | ||
``GridType.GEOGRAPHIC``. | ||
""" | ||
return self._gtype | ||
|
||
@gtype.setter | ||
def gtype(self, value): | ||
if value not in {0, 1}: | ||
# Can be simplified to `if value not in GridType` after requiring Python 3.12+. | ||
if value not in GridType.__members__.values(): | ||
msg = ( | ||
f"Invalid coordinate system type: {value}, should be " | ||
"either 0 for Cartesian or 1 for Geographic." | ||
f"Invalid grid coordinate system type: '{value}'. " | ||
"Should be either GridType.CARTESIAN or GridType.GEOGRAPHIC." | ||
) | ||
raise GMTInvalidInput(msg) | ||
self._gtype = value | ||
self._gtype = GridType(value) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.