diff --git a/pyproject.toml b/pyproject.toml index 119c2bb0d..737e2d328 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,35 +22,43 @@ requires-python = ">=3.10" # minimal dependencies start dependencies = [ "antimeridian", - "cartopy", "dask[dataframe]", - "datashader", - "geoviews", - "holoviews", - "matplotlib", - "matplotlib-inline", "netcdf4", "numba", "numpy", "pandas", - "pyarrow", - "requests", "scikit-learn", "scipy", "shapely", - "spatialpandas", - "geopandas", "xarray", - "hvplot", - "healpix", + "hvplot", # TODO: move to viz after PR #1541 gets merged. "polars", - "pyproj" ] # minimal dependencies end [project.optional-dependencies] -complete = ["uxarray[dev]"] -dev = ['pathlib', 'pre_commit', 'pytest', 'pytest-cov', 'ruff', 'asv'] +all = ["uxarray[dev,geo,viz]"] +complete = ["uxarray[dev,geo,viz]"] # alias to "all" +dev = [ + 'asv', + 'pooch', + 'pre_commit', + 'pytest', + 'pytest-cov', + 'ruff', +] +geo = [ + "geopandas", + "healpix", + "pyproj", + "spatialpandas", +] +viz = [ + "cartopy", + "geoviews", + "holoviews", + "matplotlib", +] [project.urls] Documentation = "https://uxarray.readthedocs.io/" diff --git a/test/io/test_structured.py b/test/io/test_structured.py index f53e3241f..2ecfbc652 100644 --- a/test/io/test_structured.py +++ b/test/io/test_structured.py @@ -2,6 +2,9 @@ import xarray as xr import pytest +# import pooch # not necessary to actually import, but tests would likely fail if this fails. +# (commented here for future reference, since pooch is included in uxarray[dev] +# dependencies, but not imported explicitly anywhere in uxarray.) @pytest.mark.parametrize("ds_name", ["air_temperature", "ersstv5"]) def test_read_structured_grid_from_ds(ds_name): diff --git a/uxarray/core/dataarray.py b/uxarray/core/dataarray.py index a2b55a796..177f888bb 100644 --- a/uxarray/core/dataarray.py +++ b/uxarray/core/dataarray.py @@ -5,10 +5,8 @@ from typing import TYPE_CHECKING, Any, Hashable, Literal, Mapping, Optional from warnings import warn -import cartopy.crs as ccrs import numpy as np import xarray as xr -from cartopy.mpl.geoaxes import GeoAxes from xarray.core import dtypes from xarray.core.options import OPTIONS from xarray.core.utils import UncachedAccessor @@ -37,6 +35,9 @@ from uxarray.subset import DataArraySubsetAccessor if TYPE_CHECKING: + import cartopy.crs as ccrs + from cartopy.mpl.geoaxes import GeoAxes + from uxarray.core.dataset import UxDataset @@ -462,6 +463,8 @@ def to_raster( >>> ax.imshow(raster, origin="lower", extent=ax.get_xlim() + ax.get_ylim()) """ + from cartopy.mpl.geoaxes import GeoAxes + from uxarray.constants import INT_DTYPE from uxarray.plot.matplotlib import ( _ensure_dimensions, diff --git a/uxarray/cross_sections/sample.py b/uxarray/cross_sections/sample.py index 2b34b4e44..881e5dca4 100644 --- a/uxarray/cross_sections/sample.py +++ b/uxarray/cross_sections/sample.py @@ -1,6 +1,5 @@ import numpy as np from numba import njit, prange -from pyproj import Geod @njit(parallel=True) @@ -17,6 +16,8 @@ def _fill_numba(flat_orig, face_idx, n_face, n_steps): def sample_geodesic( start: tuple[float, float], end: tuple[float, float], steps: int ) -> tuple[np.ndarray, np.ndarray]: + from pyproj import Geod + lon0, lat0 = start lon1, lat1 = end diff --git a/uxarray/grid/geometry.py b/uxarray/grid/geometry.py index 6d54ec971..f1843096f 100644 --- a/uxarray/grid/geometry.py +++ b/uxarray/grid/geometry.py @@ -397,8 +397,6 @@ def _build_corrected_polygon_shells(polygon_shells): _corrected_shells_to_original_faces : np.ndarray Original indices used to map the corrected polygon shells to their entries in face nodes """ - - # import optional dependencies import antimeridian from shapely import Polygon diff --git a/uxarray/grid/grid.py b/uxarray/grid/grid.py index f4f4d96ff..ad75d9dfa 100644 --- a/uxarray/grid/grid.py +++ b/uxarray/grid/grid.py @@ -1,10 +1,11 @@ +from __future__ import annotations + import copy import os from html import escape -from typing import Optional, Sequence +from typing import TYPE_CHECKING, Optional, Sequence from warnings import warn -import cartopy.crs as ccrs import numpy as np import xarray as xr from xarray.core.options import OPTIONS @@ -99,6 +100,9 @@ from uxarray.plot.accessor import GridPlotAccessor from uxarray.subset import GridSubsetAccessor +if TYPE_CHECKING: + import cartopy.crs as ccrs + class Grid: """Represents a two-dimensional unstructured grid encoded following the diff --git a/uxarray/io/_healpix.py b/uxarray/io/_healpix.py index 21fe87f7c..3e6bdfef7 100644 --- a/uxarray/io/_healpix.py +++ b/uxarray/io/_healpix.py @@ -1,6 +1,5 @@ from typing import Any -import healpix as hp import numpy as np import polars as pl import xarray as xr @@ -68,6 +67,8 @@ def pix2corner_ang( ---- This will be updated when https://github.com/ntessore/healpix/issues/66 is implemented. """ + import healpix as hp + if nest: fu = hp._chp.nest2ang_uv else: @@ -102,6 +103,8 @@ def _pixels_to_ugrid(zoom, nest): A dataset containing pixel longitude and latitude coordinates along with related attributes. """ + import healpix as hp + ds = xr.Dataset() nside = hp.order2nside(zoom) diff --git a/uxarray/plot/utils.py b/uxarray/plot/utils.py index e97919d6c..83b032ce8 100644 --- a/uxarray/plot/utils.py +++ b/uxarray/plot/utils.py @@ -1,6 +1,3 @@ -import holoviews as hv - - class HoloviewsBackend: """Utility class to compare and set a HoloViews plotting backend for visualization.""" @@ -16,6 +13,7 @@ def assign(self, backend: str): backend : str Plotting backend to use, one of 'matplotlib', 'bokeh' """ + import holoviews as hv if self.matplotlib_backend is None: import matplotlib as mpl diff --git a/uxarray/remap/weights.py b/uxarray/remap/weights.py index 9b0c233bf..4fdee8cdd 100644 --- a/uxarray/remap/weights.py +++ b/uxarray/remap/weights.py @@ -4,13 +4,16 @@ from dataclasses import dataclass from os import PathLike from pathlib import Path +from typing import TYPE_CHECKING import numpy as np import xarray as xr -from scipy import sparse from uxarray.core.utils import _open_dataset_with_fallback +if TYPE_CHECKING: + from scipy import sparse + # LRU-bounded cache for loaded remap operators. _WEIGHTS_CACHE_MAXSIZE = 32 _WEIGHTS_CACHE: "OrderedDict[tuple[str, int, int], RemapWeights]" = OrderedDict() @@ -83,6 +86,8 @@ class RemapWeights: @classmethod def from_file(cls, filename_or_obj: str | PathLike[str] | xr.Dataset): """Load a standard sparse remap-weight file into memory once.""" + from scipy import sparse + if isinstance(filename_or_obj, xr.Dataset): ds = filename_or_obj close_ds = False