Skip to content

Commit 65995f6

Browse files
Add function to import seafloor crustal age dataset (#1471)
New function to download the Earth seafloor age datasets at various resolutions. This is the `@earth_age` grids on the GMT remote data server. * add earth_age.py * Add test_datasets_earth_age.py * add seafloor age datasets to helpers/testing.py * add load_earth_age to index.rst * add 05m grid tile for caching Co-authored-by: Wei Ji <[email protected]>
1 parent 2bad7f3 commit 65995f6

File tree

5 files changed

+196
-0
lines changed

5 files changed

+196
-0
lines changed

doc/api/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ and store them in the GMT cache folder.
170170
.. autosummary::
171171
:toctree: generated
172172

173+
datasets.load_earth_age
173174
datasets.load_earth_relief
174175
datasets.load_fractures_compilation
175176
datasets.load_hotspots

pygmt/datasets/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#
33
# Load sample data included with GMT (downloaded from the GMT cache server).
44

5+
from pygmt.datasets.earth_age import load_earth_age
56
from pygmt.datasets.earth_relief import load_earth_relief
67
from pygmt.datasets.samples import (
78
load_fractures_compilation,

pygmt/datasets/earth_age.py

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
"""
2+
Function to download the Earth seafloor age datasets from the GMT data server,
3+
and load as :class:`xarray.DataArray`.
4+
5+
The grids are available in various resolutions.
6+
"""
7+
from pygmt.exceptions import GMTInvalidInput
8+
from pygmt.helpers import kwargs_to_strings
9+
from pygmt.io import load_dataarray
10+
from pygmt.src import grdcut, which
11+
12+
13+
@kwargs_to_strings(region="sequence")
14+
def load_earth_age(resolution="01d", region=None, registration=None):
15+
r"""
16+
Load Earth seafloor crustal ages in various resolutions.
17+
18+
The grids are downloaded to a user data directory
19+
(usually ``~/.gmt/server/earth/earth_age/``) the first time you invoke
20+
this function. Afterwards, it will load the grid from the data directory.
21+
So you'll need an internet connection the first time around.
22+
23+
These grids can also be accessed by passing in the file name
24+
**@earth_age**\_\ *res*\[_\ *reg*] to any grid plotting/processing
25+
function. *res* is the grid resolution (see below), and *reg* is grid
26+
registration type (**p** for pixel registration or **g** for gridline
27+
registration).
28+
29+
Refer to
30+
:gmt-docs:`datasets/remote-data.html#global-earth-seafloor-crustal-age-grids`
31+
for more details.
32+
33+
Parameters
34+
----------
35+
resolution : str
36+
The grid resolution. The suffix ``d`` and ``m`` stand for
37+
arc-degree, arc-minute and arc-second. It can be ``'01d'``, ``'30m'``,
38+
``'20m'``, ``'15m'``, ``'10m'``, ``'06m'``, ``'05m'``, ``'04m'``,
39+
``'03m'``, ``'02m'``, or ``'01m'``.
40+
41+
region : str or list
42+
The subregion of the grid to load, in the forms of a list
43+
[*xmin*, *xmax*, *ymin*, *ymax*] or a string *xmin/xmax/ymin/ymax*.
44+
Required for grids with resolutions higher than 5
45+
arc-minute (i.e., ``05m``).
46+
47+
registration : str
48+
Grid registration type. Either ``pixel`` for pixel registration or
49+
``gridline`` for gridline registration. Default is ``None``, where
50+
a pixel-registered grid is returned unless only the
51+
gridline-registered grid is available.
52+
53+
Returns
54+
-------
55+
grid : :class:`xarray.DataArray`
56+
The Earth seafloor crustal age grid. Coordinates are latitude and
57+
longitude in degrees. Age is in millions of years (Myr).
58+
59+
Notes
60+
-----
61+
The :class:`xarray.DataArray` grid doesn't support slice operation, for
62+
Earth seafloor crustal age with resolutions of 5 arc-minutes or higher,
63+
which are stored as smaller tiles.
64+
"""
65+
66+
# earth seafloor crust age data stored as single grids for low resolutions
67+
non_tiled_resolutions = ["01d", "30m", "20m", "15m", "10m", "06m"]
68+
# earth seafloor crust age data stored as tiles for high resolutions
69+
tiled_resolutions = ["05m", "04m", "03m", "02m", "01m"]
70+
71+
if registration in ("pixel", "gridline", None):
72+
# If None, let GMT decide on Pixel/Gridline type
73+
reg = f"_{registration[0]}" if registration else ""
74+
else:
75+
raise GMTInvalidInput(
76+
f"Invalid grid registration: '{registration}', should be either "
77+
"'pixel', 'gridline' or None. Default is None, where a "
78+
"pixel-registered grid is returned unless only the "
79+
"gridline-registered grid is available."
80+
)
81+
82+
if resolution not in non_tiled_resolutions + tiled_resolutions:
83+
raise GMTInvalidInput(f"Invalid Earth relief resolution '{resolution}'.")
84+
85+
# Choose earth relief data prefix
86+
earth_age_prefix = "earth_age_"
87+
88+
# different ways to load tiled and non-tiled earth relief data
89+
# Known issue: tiled grids don't support slice operation
90+
# See https://github.com/GenericMappingTools/pygmt/issues/524
91+
if region is None:
92+
if resolution not in non_tiled_resolutions:
93+
raise GMTInvalidInput(
94+
f"'region' is required for Earth age resolution '{resolution}'."
95+
)
96+
fname = which(f"@earth_age_{resolution}{reg}", download="a")
97+
grid = load_dataarray(fname, engine="netcdf4")
98+
else:
99+
grid = grdcut(f"@{earth_age_prefix}{resolution}{reg}", region=region)
100+
101+
# Add some metadata to the grid
102+
grid.name = "seafloor_age"
103+
grid.attrs["long_name"] = "age of seafloor crust"
104+
grid.attrs["units"] = "Myr"
105+
grid.attrs["vertical_datum"] = "EMG96"
106+
grid.attrs["horizontal_datum"] = "WGS84"
107+
# Remove the actual range because it gets outdated when indexing the grid,
108+
# which causes problems when exporting it to netCDF for usage on the
109+
# command-line.
110+
grid.attrs.pop("actual_range")
111+
for coord in grid.coords:
112+
grid[coord].attrs.pop("actual_range")
113+
return grid

pygmt/helpers/testing.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,9 @@ def download_test_data():
165165
"@N35E135.earth_relief_03s_g.nc",
166166
"@N37W120.earth_relief_03s_g.nc",
167167
"@N00W090.earth_relief_03m_p.nc",
168+
# Earth seafloor age grids
169+
"@earth_age_01d_g",
170+
"S90W180.earth_age_05m_g.nc" # Specific grid for 05m test
168171
# Other cache files
169172
"@EGM96_to_36.txt",
170173
"@fractures_06.txt",
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
"""
2+
Test basic functionality for loading Earth seafloor crust age datasets.
3+
"""
4+
import numpy as np
5+
import numpy.testing as npt
6+
import pytest
7+
from pygmt.datasets import load_earth_age
8+
from pygmt.exceptions import GMTInvalidInput
9+
10+
11+
def test_earth_age_fails():
12+
"""
13+
Make sure earth_age fails for invalid resolutions.
14+
"""
15+
resolutions = "1m 1d bla 60d 001m 03".split()
16+
resolutions.append(60)
17+
for resolution in resolutions:
18+
with pytest.raises(GMTInvalidInput):
19+
load_earth_age(resolution=resolution)
20+
21+
22+
def test_earth_age_incorrect_registration():
23+
"""
24+
Test loading earth_age with incorrect registration type.
25+
"""
26+
with pytest.raises(GMTInvalidInput):
27+
load_earth_age(registration="improper_type")
28+
29+
30+
def test_earth_age_01d():
31+
"""
32+
Test some properties of the earth age 01d data.
33+
"""
34+
data = load_earth_age(resolution="01d", registration="gridline")
35+
assert data.shape == (181, 361)
36+
npt.assert_allclose(data.lat, np.arange(-90, 91, 1))
37+
npt.assert_allclose(data.lon, np.arange(-180, 181, 1))
38+
npt.assert_allclose(data.min(), 0.167381, rtol=1e-5)
39+
npt.assert_allclose(data.max(), 338.0274, rtol=1e-5)
40+
41+
42+
def test_earth_age_01d_with_region():
43+
"""
44+
Test loading low-resolution earth age with 'region'.
45+
"""
46+
data = load_earth_age(
47+
resolution="01d", region=[-10, 10, -5, 5], registration="gridline"
48+
)
49+
assert data.shape == (11, 21)
50+
npt.assert_allclose(data.lat, np.arange(-5, 6, 1))
51+
npt.assert_allclose(data.lon, np.arange(-10, 11, 1))
52+
npt.assert_allclose(data.min(), 11.293945)
53+
npt.assert_allclose(data.max(), 125.1189)
54+
55+
56+
def test_earth_age_05m_with_region():
57+
"""
58+
Test loading a subregion of high-resolution earth age.
59+
"""
60+
data = load_earth_age(
61+
resolution="05m", region=[-50, -40, 20, 30], registration="gridline"
62+
)
63+
assert data.coords["lat"].data.min() == 20.0
64+
assert data.coords["lat"].data.max() == 30.0
65+
assert data.coords["lon"].data.min() == -50.0
66+
assert data.coords["lon"].data.max() == -40.0
67+
npt.assert_allclose(data.data.min(), 0.040000916)
68+
npt.assert_allclose(data.data.max(), 46.530003)
69+
assert data.sizes["lat"] == 121
70+
assert data.sizes["lon"] == 121
71+
72+
73+
def test_earth_age_05m_without_region():
74+
"""
75+
Test loading high-resolution earth age without passing 'region'.
76+
"""
77+
with pytest.raises(GMTInvalidInput):
78+
load_earth_age("05m")

0 commit comments

Comments
 (0)