diff --git a/hvplot/converter.py b/hvplot/converter.py index 0d225a94a..155efad2e 100644 --- a/hvplot/converter.py +++ b/hvplot/converter.py @@ -76,6 +76,7 @@ process_derived_datetime_pandas, _convert_col_names_to_str, import_datashader, + import_geoviews, is_mpl_cmap, ) from .utilities import hvplot_extension @@ -647,6 +648,10 @@ def __init__( self.dynamic = dynamic self.geo = any([geo, crs, global_extent, projection, project, coastline, features]) + # Try importing geoviews if geo-features requested + if self.geo or self.datatype == 'geopandas': + import_geoviews() + self.crs = self._process_crs(data, crs) if self.geo else None self.output_projection = self.crs self.project = project @@ -656,17 +661,6 @@ def __init__( self.tiles_opts = tiles_opts or {} self.sort_date = sort_date - # Import geoviews if geo-features requested - if self.geo or self.datatype == 'geopandas': - try: - import geoviews # noqa - except ImportError: - raise ImportError( - 'In order to use geo-related features ' - 'the geoviews library must be available. ' - 'It can be installed with:\n conda ' - 'install geoviews' - ) if self.geo: if self.kind not in self._geo_types: param.main.param.warning( diff --git a/hvplot/tests/testui.py b/hvplot/tests/testui.py index ccbbb3cca..ae3aa8816 100644 --- a/hvplot/tests/testui.py +++ b/hvplot/tests/testui.py @@ -1,5 +1,6 @@ import re from textwrap import dedent +from unittest.mock import patch import numpy as np import holoviews as hv @@ -415,3 +416,9 @@ def test_max_rows_sample(): ui = hvplot.explorer(df, x='x', y='y', by=['#'], kind='scatter') assert len(ui._data) == MAX_ROWS assert not ui._data.equals(df.head(MAX_ROWS)) + + +def test_explorer_geo_no_import_error_when_false(): + da = ds_air_temperature['air'].isel(time=0) + with patch('hvplot.util.import_geoviews', return_value=None): + assert hvplot.explorer(da, x='lon', y='lat', geo=False) diff --git a/hvplot/tests/testutil.py b/hvplot/tests/testutil.py index 4f9d39ef8..01a46ce82 100644 --- a/hvplot/tests/testutil.py +++ b/hvplot/tests/testutil.py @@ -18,6 +18,7 @@ from hvplot.util import ( check_crs, + import_geoviews, is_list_like, process_crs, process_xarray, @@ -383,3 +384,8 @@ def test_is_geodataframe_spatialpandas_dask(): def test_is_geodataframe_classic_dataframe(): df = pd.DataFrame({'geometry': [None, None], 'name': ['A', 'B']}) assert not is_geodataframe(df) + + +@pytest.mark.geo +def test_geoviews_is_available(): + assert import_geoviews() diff --git a/hvplot/ui.py b/hvplot/ui.py index bf2044e0e..54fcb23a3 100644 --- a/hvplot/ui.py +++ b/hvplot/ui.py @@ -10,7 +10,7 @@ from .converter import HoloViewsConverter as _hvConverter from .plotting import hvPlot as _hvPlot -from .util import is_geodataframe, is_xarray, instantiate_crs_str +from .util import is_geodataframe, is_xarray, instantiate_crs_str, import_geoviews # Defaults KINDS = { @@ -361,17 +361,10 @@ class Geographic(Controls): _widgets_kwargs = {'geo': {'type': pn.widgets.Toggle}} def __init__(self, data, **params): - gv_available = False - try: - import geoviews # noqa - - gv_available = True - except ImportError: - pass - geo_params = GEO_KEYS + ['geo'] - if not gv_available and any(p in params for p in geo_params): - raise ImportError('GeoViews must be installed to enable the geographic options.') + gv_available = None + if any(params.get(p) for p in geo_params): + gv_available = import_geoviews() super().__init__(data, **params) if not gv_available: for p in geo_params: diff --git a/hvplot/util.py b/hvplot/util.py index db347c608..40ecae2cc 100644 --- a/hvplot/util.py +++ b/hvplot/util.py @@ -729,6 +729,18 @@ def import_datashader(): return datashader +def import_geoviews(): + geoviews = None + try: + import geoviews + except ModuleNotFoundError: + raise ModuleNotFoundError( + 'The `geoviews` package must be installed in order to use ' + 'geographic features. Install it with pip or conda.' + ) from None + return geoviews + + def relabel(hv_obj, **kwargs): """Conditionally relabel a HoloViews object""" if kwargs: