diff --git a/docs/projections.py b/docs/projections.py index 6310fd584..66dc30c37 100644 --- a/docs/projections.py +++ b/docs/projections.py @@ -289,11 +289,10 @@ # when they are omitted (e.g., ``lon0=0`` as the default for most projections). # # .. warning:: -# The `basemap`_ package is now being actively maintained again with a short hiatus for a few years. We originally -# included basemap support because its gridline labeling was more powerful -# than cartopy gridline labeling. While cartopy gridline labeling has -# significantly improved since version 0.18, UltraPlot continues to support -# both mapping libraries to give users flexibility in their visualization choices. +# The basemap backend is deprecated as of version 3.0.0 and may be removed in a +# future release. Please use the cartopy backend instead. Cartopy gridline +# labeling has significantly improved since version 0.18 and now covers the +# features we originally included basemap support for. # %% import ultraplot as uplt diff --git a/ultraplot/_subplots.py b/ultraplot/_subplots.py index b0594973b..4cd34ef03 100644 --- a/ultraplot/_subplots.py +++ b/ultraplot/_subplots.py @@ -66,10 +66,7 @@ def parse_backend(backend=None, basemap=None): Handle deprecation of basemap and cartopy package. """ if backend == "basemap": - warnings._warn_ultraplot( - f"{backend=} will be deprecated in next major release (v2.0). " - "See https://github.com/Ultraplot/ultraplot/pull/243" - ) + constructor._warn_basemap_deprecated() return backend def parse_proj( @@ -108,6 +105,7 @@ def parse_proj( ): name = "ultraplot_basemap" kwargs["map_projection"] = proj + constructor._warn_basemap_deprecated() if name is None and isinstance(proj, str): try: diff --git a/ultraplot/axes/base.py b/ultraplot/axes/base.py index 735e99bee..19a429d15 100644 --- a/ultraplot/axes/base.py +++ b/ultraplot/axes/base.py @@ -104,6 +104,10 @@ backend : {'cartopy', 'basemap'}, default: :rc:`geo.backend` Whether to use `~mpl_toolkits.basemap.Basemap` or `~cartopy.crs.Projection` for map projections. + + .. deprecated:: 3.0.0 + The ``'basemap'`` backend is deprecated and may be removed in a + future release. Please use the ``'cartopy'`` backend instead. """ docstring._snippet_manager["axes.proj"] = _proj_docstring docstring._snippet_manager["axes.proj_kw"] = _proj_kw_docstring diff --git a/ultraplot/constructor.py b/ultraplot/constructor.py index 04d315f69..eb8d7383f 100644 --- a/ultraplot/constructor.py +++ b/ultraplot/constructor.py @@ -1484,6 +1484,17 @@ def Scale(scale, *args, **kwargs): return scale(*args, **kwargs) +def _warn_basemap_deprecated(): + """ + Warn that the basemap backend is deprecated. + """ + warnings._warn_ultraplot( + "The basemap backend was deprecated in version 3.0.0 and may be removed " + "in a future release. Please use the cartopy backend instead. " + "See https://github.com/Ultraplot/ultraplot/pull/243" + ) + + def Proj( name, backend=None, @@ -1574,6 +1585,10 @@ def Proj( backend : {'cartopy', 'basemap'}, default: :rc:`geo.backend` Whether to return a cartopy `~cartopy.crs.Projection` instance or a basemap `~mpl_toolkits.basemap.Basemap` instance. + + .. deprecated:: 3.0.0 + The ``'basemap'`` backend is deprecated and may be removed in a + future release. Please use the ``'cartopy'`` backend instead. lon0, lat0 : float, optional The central projection longitude and latitude. These are translated to `central_longitude`, `central_latitude` for cartopy projections. @@ -1670,6 +1685,8 @@ def Proj( f"Unexpected projection {name!r}. Must be PROJ string name, " "cartopy.crs.Projection, or mpl_toolkits.basemap.Basemap." ) + if backend == "basemap": + _warn_basemap_deprecated() for key_proj, key_cartopy, value in ( ("lon_0", "central_longitude", lon0), ("lat_0", "central_latitude", lat0), @@ -1692,6 +1709,8 @@ def Proj( warnings._warn_ultraplot(f"Ignoring Proj() keyword arg(s): {kwargs!r}.") proj = name backend = "cartopy" if is_crs else "basemap" + if is_basemap: + _warn_basemap_deprecated() # Cartopy name # NOTE: Error message matches basemap invalid projection message diff --git a/ultraplot/internals/rcsetup.py b/ultraplot/internals/rcsetup.py index 6493df60d..7a098d4aa 100644 --- a/ultraplot/internals/rcsetup.py +++ b/ultraplot/internals/rcsetup.py @@ -1764,7 +1764,9 @@ def _validator_accepts(validator, value): "cartopy", _validate_belongs("cartopy", "basemap"), "The backend used for `~ultraplot.axes.GeoAxes`. Must be " - "either 'cartopy' or 'basemap'.", + "either 'cartopy' or 'basemap'. " + ".. deprecated:: 3.0.0 The 'basemap' backend is deprecated and may be " + "removed in a future release. Please use 'cartopy' instead.", ), "geo.extent": ( "globe", diff --git a/ultraplot/tests/test_constructor_helpers_extra.py b/ultraplot/tests/test_constructor_helpers_extra.py index 7d75dc3f6..08065b1d6 100644 --- a/ultraplot/tests/test_constructor_helpers_extra.py +++ b/ultraplot/tests/test_constructor_helpers_extra.py @@ -2,6 +2,7 @@ """Additional branch coverage for constructor helpers.""" import importlib +import warnings import cycler import matplotlib.colors as mcolors @@ -195,3 +196,35 @@ def test_proj_constructor_branches(): constructor.Proj("merc", backend="cartopy", round=True) with pytest.raises(ValueError, match="unknown cartopy projection class"): constructor.Proj("not-a-proj", backend="cartopy") + + +def test_proj_basemap_backend_deprecated(): + pytest.importorskip("mpl_toolkits.basemap") + with pytest.warns(UltraPlotWarning, match="deprecated in version 3\\.0\\.0"): + proj = constructor.Proj("npstere", backend="basemap") + assert proj._proj_backend == "basemap" + + +def test_proj_basemap_instance_deprecated(): + pytest.importorskip("mpl_toolkits.basemap") + with pytest.warns(UltraPlotWarning, match="basemap backend was deprecated"): + basemap_proj = constructor.Proj("npstere", backend="basemap") + proj = constructor.Proj(basemap_proj) + assert proj is basemap_proj + assert proj._proj_backend == "basemap" + + +def test_proj_basemap_rc_backend_deprecated(): + pytest.importorskip("mpl_toolkits.basemap") + with uplt.rc.context({"geo.backend": "basemap"}): + with pytest.warns(UltraPlotWarning, match="basemap backend was deprecated"): + proj = constructor.Proj("npstere") + assert proj._proj_backend == "basemap" + + +def test_proj_cartopy_backend_no_warning(): + pytest.importorskip("cartopy") + with warnings.catch_warnings(): + warnings.simplefilter("error", UltraPlotWarning) + proj = constructor.Proj("npstere", backend="cartopy") + assert proj._proj_backend == "cartopy" diff --git a/ultraplot/tests/test_subplot_manager.py b/ultraplot/tests/test_subplot_manager.py index 2b0aed87a..46e6bb06d 100644 --- a/ultraplot/tests/test_subplot_manager.py +++ b/ultraplot/tests/test_subplot_manager.py @@ -33,7 +33,7 @@ def test_gridspec_setter_accepts_ultraplot(): def test_parse_backend_basemap_warns(): """parse_backend emits a deprecation warning for basemap.""" - with pytest.warns(match="basemap"): + with pytest.warns(match="basemap backend was deprecated"): SubplotManager.parse_backend(backend="basemap") @@ -43,6 +43,25 @@ def test_parse_backend_passthrough(): assert SubplotManager.parse_backend(backend=None) is None +def test_subplots_basemap_backend_warns(): + """Requesting the basemap backend emits a deprecation warning.""" + pytest.importorskip("mpl_toolkits.basemap") + with pytest.warns(match="basemap backend was deprecated"): + fig, axs = uplt.subplots(proj="cyl", backend="basemap") + assert axs[0]._name == "basemap" + uplt.close(fig) + + +def test_subplots_basemap_instance_warns(): + """Passing a Basemap instance as proj emits a deprecation warning.""" + pytest.importorskip("mpl_toolkits.basemap") + with pytest.warns(match="basemap backend was deprecated"): + proj = uplt.Proj("npstere", backend="basemap") + fig, axs = uplt.subplots(proj=proj) + assert axs[0]._name == "basemap" + uplt.close(fig) + + def test_add_subplot_integer_arg(): """add_subplot(111) creates a single subplot.""" fig = uplt.figure()