Skip to content

Commit 66f258a

Browse files
willschlitzerweiji14seisman
authored
Add function to load Blue Marble dataset (#2235)
Function to load the `@earth_day_` NASA Blue Marble mosaic RGB images stored as GeoTIFF files. --------- Co-authored-by: Wei Ji <[email protected]> Co-authored-by: Dongdong Tian <[email protected]>
1 parent f97c3a4 commit 66f258a

File tree

6 files changed

+171
-11
lines changed

6 files changed

+171
-11
lines changed

doc/api/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ and store them in GMT's user data directory.
218218
:toctree: generated
219219

220220
datasets.list_sample_data
221+
datasets.load_blue_marble
221222
datasets.load_earth_age
222223
datasets.load_earth_free_air_anomaly
223224
datasets.load_earth_geoid

pygmt/datasets/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"""
66

77
from pygmt.datasets.earth_age import load_earth_age
8+
from pygmt.datasets.earth_day import load_blue_marble
89
from pygmt.datasets.earth_free_air_anomaly import load_earth_free_air_anomaly
910
from pygmt.datasets.earth_geoid import load_earth_geoid
1011
from pygmt.datasets.earth_magnetic_anomaly import load_earth_magnetic_anomaly

pygmt/datasets/earth_day.py

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
"""
2+
Function to download the NASA Blue Marble image datasets from the GMT data server, and
3+
load as :class:`xarray.DataArray`.
4+
5+
The images are available in various resolutions.
6+
"""
7+
8+
from collections.abc import Sequence
9+
from typing import Literal
10+
11+
import xarray as xr
12+
from pygmt.datasets.load_remote_dataset import _load_remote_dataset
13+
14+
__doctest_skip__ = ["load_blue_marble"]
15+
16+
17+
def load_blue_marble(
18+
resolution: Literal[
19+
"01d",
20+
"30m",
21+
"20m",
22+
"15m",
23+
"10m",
24+
"06m",
25+
"05m",
26+
"04m",
27+
"03m",
28+
"02m",
29+
"01m",
30+
"30s",
31+
] = "01d",
32+
region: Sequence[float] | str | None = None,
33+
) -> xr.DataArray:
34+
r"""
35+
Load NASA Blue Marble images in various resolutions.
36+
37+
.. figure:: https://www.generic-mapping-tools.org/remote-datasets/_images/GMT_earth_daynight.jpg
38+
:width: 80%
39+
:align: center
40+
41+
Earth day/night dataset.
42+
43+
The images are downloaded to a user data directory (usually
44+
``~/.gmt/server/earth/earth_day/``) the first time you invoke this function.
45+
Afterwards, it will load the image from the data directory. So you'll need an
46+
internet connection the first time around.
47+
48+
These images can also be accessed by passing in the file name
49+
**@earth_day**\_\ *res* to any image processing function or plotting method. *res*
50+
is the image resolution (see below).
51+
52+
Refer to :gmt-datasets:`earth-daynight.html` for more details about available
53+
datasets, including version information and references.
54+
55+
Parameters
56+
----------
57+
resolution
58+
The image resolution. The suffix ``d``, ``m``, and ``s`` stand for arc-degree,
59+
arc-minute, and arc-second.
60+
61+
region
62+
The subregion of the image to load, in the form of a sequence [*xmin*, *xmax*,
63+
*ymin*, *ymax*].
64+
65+
Returns
66+
-------
67+
image
68+
The NASA Blue Marble image. Coordinates are latitude and longitude in degrees.
69+
70+
Note
71+
----
72+
The registration and coordinate system type of the returned
73+
:class:`xarray.DataArray` image can be accessed via the GMT accessors (i.e.,
74+
``image.gmt.registration`` and ``image.gmt.gtype`` respectively). However, these
75+
properties may be lost after specific image operations (such as slicing) and will
76+
need to be manually set before passing the image to any PyGMT data processing or
77+
plotting functions. Refer to :class:`pygmt.GMTDataArrayAccessor` for detailed
78+
explanations and workarounds.
79+
80+
Examples
81+
--------
82+
83+
>>> from pygmt.datasets import load_blue_marble
84+
>>> # load the default image (pixel-registered 1 arc-degree image)
85+
>>> image = load_blue_marble()
86+
"""
87+
image = _load_remote_dataset(
88+
name="earth_day",
89+
prefix="earth_day",
90+
resolution=resolution,
91+
region=region,
92+
registration="pixel",
93+
)
94+
# If rioxarray is installed, set the coordinate reference system
95+
if hasattr(image, "rio"):
96+
image = image.rio.write_crs(input_crs="OGC:CRS84")
97+
return image

pygmt/datasets/load_remote_dataset.py

+26-4
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,25 @@ class GMTRemoteDataset(NamedTuple):
7878
"01m": Resolution("01m", registrations=["gridline"], tiled=True),
7979
},
8080
),
81+
"earth_day": GMTRemoteDataset(
82+
description="NASA Day Images",
83+
units=None,
84+
extra_attributes={"long_name": "blue_marble", "horizontal_datum": "WGS84"},
85+
resolutions={
86+
"01d": Resolution("01d", registrations=["pixel"]),
87+
"30m": Resolution("30m", registrations=["pixel"]),
88+
"20m": Resolution("20m", registrations=["pixel"]),
89+
"15m": Resolution("15m", registrations=["pixel"]),
90+
"10m": Resolution("10m", registrations=["pixel"]),
91+
"06m": Resolution("06m", registrations=["pixel"]),
92+
"05m": Resolution("05m", registrations=["pixel"]),
93+
"04m": Resolution("04m", registrations=["pixel"]),
94+
"03m": Resolution("03m", registrations=["pixel"]),
95+
"02m": Resolution("02m", registrations=["pixel"]),
96+
"01m": Resolution("01m", registrations=["pixel"]),
97+
"30s": Resolution("30s", registrations=["pixel"]),
98+
},
99+
),
81100
"earth_faa": GMTRemoteDataset(
82101
description="IGPP Earth free-air anomaly",
83102
units="mGal",
@@ -409,15 +428,18 @@ def _load_remote_dataset(
409428
f"'region' is required for {dataset.description} resolution '{resolution}'."
410429
)
411430

412-
# Currently, only grids are supported. Will support images in the future.
413-
kwdict = {"T": "g", "R": region} # region can be None
431+
kind = "image" if name in {"earth_day"} else "grid"
432+
kwdict = {
433+
"R": region, # region can be None
434+
"T": "i" if kind == "image" else "g",
435+
}
414436
with Session() as lib:
415-
with lib.virtualfile_out(kind="grid") as voutgrd:
437+
with lib.virtualfile_out(kind=kind) as voutgrd:
416438
lib.call_module(
417439
module="read",
418440
args=[fname, voutgrd, *build_arg_list(kwdict)],
419441
)
420-
grid = lib.virtualfile_to_raster(outgrid=None, vfname=voutgrd)
442+
grid = lib.virtualfile_to_raster(kind=kind, outgrid=None, vfname=voutgrd)
421443

422444
# Full path to the grid if not tiled grids.
423445
source = which(fname, download="a") if not resinfo.tiled else None
+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
"""
2+
Test basic functionality for loading Blue Marble datasets.
3+
"""
4+
5+
import numpy as np
6+
import numpy.testing as npt
7+
from pygmt.datasets import load_blue_marble
8+
9+
10+
def test_blue_marble_01d():
11+
"""
12+
Test some properties of the Blue Marble 01d data.
13+
"""
14+
data = load_blue_marble(resolution="01d")
15+
assert data.name == "z"
16+
assert data.long_name == "blue_marble"
17+
assert data.attrs["horizontal_datum"] == "WGS84"
18+
assert data.attrs["description"] == "NASA Day Images"
19+
assert data.shape == (3, 180, 360)
20+
assert data.dtype == "uint8"
21+
assert data.gmt.registration == 1
22+
assert data.gmt.gtype == 1
23+
npt.assert_allclose(data.y, np.arange(89.5, -90.5, -1))
24+
npt.assert_allclose(data.x, np.arange(-179.5, 180.5, 1))
25+
npt.assert_allclose(data.min(), 10, atol=1)
26+
npt.assert_allclose(data.max(), 255, atol=1)
27+
28+
29+
def test_blue_marble_01d_with_region():
30+
"""
31+
Test loading low-resolution Blue Marble with 'region'.
32+
"""
33+
data = load_blue_marble(resolution="01d", region=[-10, 10, -5, 5])
34+
assert data.shape == (3, 10, 20)
35+
assert data.dtype == "uint8"
36+
assert data.gmt.registration == 1
37+
assert data.gmt.gtype == 1
38+
npt.assert_allclose(data.y, np.arange(4.5, -5.5, -1))
39+
npt.assert_allclose(data.x, np.arange(-9.5, 10.5, 1))
40+
npt.assert_allclose(data.min(), 10, atol=1)
41+
npt.assert_allclose(data.max(), 77, atol=1)

pygmt/tests/test_grdimage_image.py

+5-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
"""
44

55
import pytest
6-
from pygmt import Figure, which
6+
from pygmt import Figure
7+
from pygmt.datasets import load_blue_marble
78

89
rioxarray = pytest.importorskip("rioxarray")
910

@@ -14,12 +15,9 @@ def fixture_xr_image():
1415
Load the image data from Blue Marble as an xarray.DataArray with shape {"band": 3,
1516
"y": 180, "x": 360}.
1617
"""
17-
geotiff = which(fname="@earth_day_01d_p", download="c")
18-
with rioxarray.open_rasterio(filename=geotiff) as rda:
19-
if len(rda.band) == 3:
20-
xr_image = rda.load()
21-
assert xr_image.sizes == {"band": 3, "y": 180, "x": 360}
22-
return xr_image
18+
xr_image = load_blue_marble(resolution="01d")
19+
assert xr_image.sizes == {"band": 3, "y": 180, "x": 360}
20+
return xr_image
2321

2422

2523
@pytest.mark.mpl_image_compare

0 commit comments

Comments
 (0)