1- from importlib import resources
2- from pathlib import Path
31from typing import Any , Callable
42
53import numpy as np
119from xcdat .axis import get_dim_coords
1210from xcdat .regridder .accessor import obj_to_grid_ds
1311from xcdat .regridder .grid import create_grid
12+ from xcdat .utils import _get_resource_path
1413
1514logger = _setup_custom_logger (__name__ )
1615
@@ -342,10 +341,12 @@ def pcmdi_land_sea_mask(
342341 source : xr .Dataset | None = None ,
343342 source_data_var : str | None = None ,
344343) -> xr .DataArray :
345- """Generate a land-sea mask using the PCMDI method.
344+ """
345+ Generate a land-sea mask using the PCMDI method.
346346
347- This method uses a high-resolution land-sea mask and regrids it to the
348- resolution of the input DataArray. It then iteratively improves the mask.
347+ This method uses a high-resolution land-sea mask and regrids it to the resolution
348+ of the input DataArray. It then iteratively improves the mask based on specified
349+ thresholds.
349350
350351 Parameters
351352 ----------
@@ -356,18 +357,31 @@ def pcmdi_land_sea_mask(
356357 threshold2 : float, optional
357358 The second threshold for improving the mask, by default 0.3.
358359 source : xr.Dataset | None, optional
359- The Dataset containing the variable to use as the high resolution source.
360+ A custom Dataset containing the variable to use as the high-resolution source.
361+ If not provided, a default high-resolution land-sea mask is used.
360362 source_data_var : str | None, optional
361- Name of the variable in `source` to use as the high resolution source.
363+ The name of the variable in `source` to use as the high-resolution source.
364+ If `source` is not provided, this defaults to "sftlf".
362365
363366 Returns
364367 -------
365368 xr.DataArray
366- The land-sea mask.
369+ The generated land-sea mask.
370+
371+ Raises
372+ ------
373+ ValueError
374+ If `source` is provided but `source_data_var` is None.
375+
376+ Notes
377+ -----
378+ By default, the `navy_land.nc` file is used as the high-resolution land-sea mask.
379+ This file is sourced from the PCMDI (Program for Climate Model Diagnosis and
380+ Intercomparison) Metrics Package. It is available at:
381+ https://github.com/PCMDI/pcmdi_metrics/blob/main/share/data/navy_land.nc
367382
368383 Examples
369384 --------
370-
371385 Generate a land-sea mask using the PCMDI method:
372386
373387 >>> import xcdat
@@ -376,12 +390,16 @@ def pcmdi_land_sea_mask(
376390
377391 Generate a land-sea mask using the PCMDI method with custom thresholds:
378392
379- >>> land_sea_mask = xcdat.mask.pcmdi_land_sea_mask(ds["tas"], threshold1=0.3, threshold2=0.4)
393+ >>> land_sea_mask = xcdat.mask.pcmdi_land_sea_mask(
394+ ... ds["tas"], threshold1=0.3, threshold2=0.4
395+ ... )
380396
381- Generate a land-sea mask using the PCMDI method with a custom high resolution source:
397+ Generate a land-sea mask using the PCMDI method with a custom high-res source:
382398
383- >>> highres_ds = xcdata.open_dataset("/path/to/file")
384- >>> land_sea_mask = xcdat.mask.pcmdi_land_sea_mask(ds["tas"], source=highres_ds, source_data_var="highres")
399+ >>> highres_ds = xcdat.open_dataset("/path/to/file")
400+ >>> land_sea_mask = xcdat.mask.pcmdi_land_sea_mask(
401+ ... ds["tas"], source=highres_ds, source_data_var="highres"
402+ ... )
385403 """
386404 if source is not None and source_data_var is None :
387405 raise ValueError (
@@ -391,9 +409,11 @@ def pcmdi_land_sea_mask(
391409 if source is None :
392410 source_data_var = "sftlf"
393411
394- resource_path = str (_get_resource_path ("navy_land.nc" , Path . cwd () ))
412+ resource_path = str (_get_resource_path ("navy_land.nc" ))
395413
396- source = open_dataset (resource_path )
414+ # Turn off time decoding to prevent logger warning since this dataset
415+ # does not have a time axis.
416+ source = open_dataset (resource_path , decode_times = False )
397417
398418 source_regrid = source .regridder .horizontal (
399419 source_data_var , obj_to_grid_ds (da ), tool = "regrid2"
@@ -435,44 +455,6 @@ def pcmdi_land_sea_mask(
435455 return mask [source_data_var ]
436456
437457
438- def _get_resource_path (filename : str , default_path : Path | None = None ) -> Path :
439- """Get the path to a resource file.
440-
441- Parameters
442- ----------
443- filename : str
444- The name of the resource file.
445-
446- Returns
447- -------
448- Path
449- The path to the resource file.
450- """
451- if default_path is None :
452- default_path = Path .cwd ()
453-
454- resource_path : Path | None = None
455-
456- try :
457- with resources .as_file (resources .files ("xcdat" ).joinpath (filename )) as x :
458- resource_path = x
459- except (ModuleNotFoundError , FileNotFoundError ) as e :
460- logger .warning (e )
461- resource_path = None
462-
463- if resource_path and resource_path .exists ():
464- return resource_path
465-
466- resource_path = default_path / "xcdat" / filename
467-
468- if not resource_path .exists ():
469- raise RuntimeError (
470- f"Resource file { filename !r} not found in package or at { resource_path !s} ."
471- )
472-
473- return resource_path
474-
475-
476458def _is_circular (lon : xr .DataArray , lon_bnds : xr .DataArray ) -> bool :
477459 """Check if a longitude axis is circular.
478460
0 commit comments