-
Notifications
You must be signed in to change notification settings - Fork 229
Allow passing region to GMTBackendEntrypoint.open_dataset #3932
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
base: main
Are you sure you want to change the base?
Changes from all commits
12c2662
3114987
72abcaf
3a04239
1a5837a
fe6bd44
6dec9ad
c2c010c
07b2802
5557b33
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -144,7 +144,7 @@ def test_xarray_accessor_grid_source_file_not_exist(): | |
# Registration and gtype are correct. | ||
assert grid.gmt.registration == GridRegistration.PIXEL | ||
assert grid.gmt.gtype == GridType.GEOGRAPHIC | ||
# The source grid file is undefined. | ||
# The source grid file is undefined for tiled grids. | ||
assert grid.encoding.get("source") is None | ||
Comment on lines
+147
to
148
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 keep 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.
Sounds good. |
||
|
||
# For a sliced grid, fallback to default registration and gtype, because the source | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,8 +40,8 @@ def test_xarray_backend_load_dataarray(): | |
|
||
def test_xarray_backend_gmt_open_nc_grid(): | ||
""" | ||
Ensure that passing engine='gmt' to xarray.open_dataarray works for opening NetCDF | ||
grids. | ||
Ensure that passing engine='gmt' to xarray.open_dataarray works to open a netCDF | ||
grid. | ||
""" | ||
with xr.open_dataarray( | ||
"@static_earth_relief.nc", engine="gmt", raster_kind="grid" | ||
|
@@ -52,10 +52,29 @@ def test_xarray_backend_gmt_open_nc_grid(): | |
assert da.gmt.registration == GridRegistration.PIXEL | ||
|
||
|
||
def test_xarray_backend_gmt_open_nc_grid_with_region_bbox(): | ||
""" | ||
Ensure that passing engine='gmt' with a `region` argument to xarray.open_dataarray | ||
works to open a netCDF grid over a specific bounding box. | ||
""" | ||
with xr.open_dataarray( | ||
"@static_earth_relief.nc", | ||
engine="gmt", | ||
raster_kind="grid", | ||
region=[-52, -48, -18, -12], | ||
) as da: | ||
assert da.sizes == {"lat": 6, "lon": 4} | ||
npt.assert_allclose(da.lat, [-17.5, -16.5, -15.5, -14.5, -13.5, -12.5]) | ||
npt.assert_allclose(da.lon, [-51.5, -50.5, -49.5, -48.5]) | ||
assert da.dtype == "float32" | ||
assert da.gmt.gtype == GridType.GEOGRAPHIC | ||
assert da.gmt.registration == GridRegistration.PIXEL | ||
|
||
|
||
def test_xarray_backend_gmt_open_tif_image(): | ||
""" | ||
Ensure that passing engine='gmt' to xarray.open_dataarray works for opening GeoTIFF | ||
images. | ||
Ensure that passing engine='gmt' to xarray.open_dataarray works to open a GeoTIFF | ||
image. | ||
""" | ||
with xr.open_dataarray("@earth_day_01d", engine="gmt", raster_kind="image") as da: | ||
assert da.sizes == {"band": 3, "y": 180, "x": 360} | ||
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. Coordinate names are |
||
|
@@ -64,6 +83,22 @@ def test_xarray_backend_gmt_open_tif_image(): | |
assert da.gmt.registration == GridRegistration.PIXEL | ||
|
||
|
||
def test_xarray_backend_gmt_open_tif_image_with_region_iso(): | ||
""" | ||
Ensure that passing engine='gmt' with a `region` argument to xarray.open_dataarray | ||
works to open a GeoTIFF image over a specific ISO country code border. | ||
""" | ||
with xr.open_dataarray( | ||
"@earth_day_01d", engine="gmt", raster_kind="image", region="BN" | ||
) as da: | ||
assert da.sizes == {"band": 3, "lat": 2, "lon": 2} | ||
npt.assert_allclose(da.lat, [5.5, 4.5]) | ||
npt.assert_allclose(da.lon, [114.5, 115.5]) | ||
assert da.dtype == "uint8" | ||
assert da.gmt.gtype == GridType.GEOGRAPHIC | ||
assert da.gmt.registration == GridRegistration.PIXEL | ||
|
||
|
||
def test_xarray_backend_gmt_load_grd_grid(): | ||
""" | ||
Ensure that passing engine='gmt' to xarray.load_dataarray works for loading GRD | ||
|
@@ -88,9 +123,7 @@ def test_xarray_backend_gmt_read_invalid_kind(): | |
""" | ||
with pytest.raises( | ||
TypeError, | ||
match=re.escape( | ||
"GMTBackendEntrypoint.open_dataset() missing 1 required keyword-only argument: 'raster_kind'" | ||
), | ||
match=re.escape("missing a required argument: 'raster_kind'"), | ||
): | ||
xr.open_dataarray("nokind.nc", engine="gmt") | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,13 +2,14 @@ | |
An xarray backend for reading raster grid/image files using the 'gmt' engine. | ||
""" | ||
|
||
from collections.abc import Sequence | ||
from typing import Literal | ||
|
||
import xarray as xr | ||
from pygmt._typing import PathLike | ||
from pygmt.clib import Session | ||
from pygmt.exceptions import GMTInvalidInput | ||
from pygmt.helpers import build_arg_list | ||
from pygmt.helpers import build_arg_list, kwargs_to_strings | ||
from pygmt.src.which import which | ||
from xarray.backends import BackendEntrypoint | ||
|
||
|
@@ -71,15 +72,17 @@ class GMTBackendEntrypoint(BackendEntrypoint): | |
""" | ||
|
||
description = "Open raster (.grd, .nc or .tif) files in Xarray via GMT." | ||
open_dataset_parameters = ("filename_or_obj", "raster_kind") | ||
open_dataset_parameters = ("filename_or_obj", "raster_kind", "region") | ||
url = "https://pygmt.org/dev/api/generated/pygmt.GMTBackendEntrypoint.html" | ||
|
||
@kwargs_to_strings(region="sequence") | ||
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. I've been think if we should avoid using the |
||
def open_dataset( # type: ignore[override] | ||
self, | ||
filename_or_obj: PathLike, | ||
*, | ||
drop_variables=None, # noqa: ARG002 | ||
raster_kind: Literal["grid", "image"], | ||
region: Sequence[float] | str | None = None, | ||
# other backend specific keyword arguments | ||
# `chunks` and `cache` DO NOT go here, they are handled by xarray | ||
) -> xr.Dataset: | ||
|
@@ -94,14 +97,17 @@ def open_dataset( # type: ignore[override] | |
:gmt-docs:`reference/features.html#grid-file-format`. | ||
raster_kind | ||
Whether to read the file as a "grid" (single-band) or "image" (multi-band). | ||
region | ||
Optional. The subregion of the grid or image to load, in the form of a | ||
sequence [*xmin*, *xmax*, *ymin*, *ymax*] or an ISO country code. | ||
""" | ||
if raster_kind not in {"grid", "image"}: | ||
msg = f"Invalid raster kind: '{raster_kind}'. Valid values are 'grid' or 'image'." | ||
raise GMTInvalidInput(msg) | ||
|
||
with Session() as lib: | ||
with lib.virtualfile_out(kind=raster_kind) as voutfile: | ||
kwdict = {"T": {"grid": "g", "image": "i"}[raster_kind]} | ||
kwdict = {"R": region, "T": {"grid": "g", "image": "i"}[raster_kind]} | ||
lib.call_module( | ||
module="read", | ||
args=[filename_or_obj, voutfile, *build_arg_list(kwdict)], | ||
|
@@ -111,9 +117,8 @@ def open_dataset( # type: ignore[override] | |
vfname=voutfile, kind=raster_kind | ||
) | ||
# Add "source" encoding | ||
source = which(fname=filename_or_obj) | ||
source: str | list = which(fname=filename_or_obj, verbose="q") | ||
raster.encoding["source"] = ( | ||
source[0] if isinstance(source, list) else source | ||
) | ||
_ = raster.gmt # Load GMTDataArray accessor information | ||
return raster.to_dataset() | ||
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. Actually, it's likely that the accessor information will be lost when converting via |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see a lot of error messages like:
This is because, in the GMT backend, we use something like
which("@earth_age_01m_g")
to get the file path, which doesn't work well for tiled grids.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, we used to do this:
i.e. only add the source for non-tiled grids, so that the accessor's
which
call doesn't report this error. I'm thinking if it's possible to either 1) silence thewhich
call (doesverbose="q"
work?), or 2) add some heuristic/logic to determine whether the source is a tiled grid before callingwhich
inGMTBackendEntrypoint
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think either works. Perhaps
verbose="q"
is easier?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in commit 5557b33