Source code for zyra.visualization.basemap

# SPDX-License-Identifier: Apache-2.0
"""Basemap helpers for Cartopy/Matplotlib renderers.

Functions here are intentionally lightweight and avoid hard dependencies
on optional tile providers. Tile support is stubbed and only enabled if
the dependency is available at runtime.
"""

from __future__ import annotations

from typing import Iterable, Optional


[docs] def add_basemap_cartopy( ax, extent: Optional[Iterable[float]] = None, *, image_path: Optional[str] = None, features: Optional[Iterable[str]] = None, alpha: float = 1.0, ): """Add a simple basemap to a Cartopy axis. Parameters ---------- ax : cartopy.mpl.geoaxes.GeoAxesSubplot Target axes with a geographic projection (PlateCarree recommended). extent : iterable of float, optional [west, east, south, north] in PlateCarree coordinates. image_path : str, optional Path to a background image to draw via ``imshow``. features : iterable of str, optional Feature names to add: any of {"coastline", "borders", "gridlines"}. alpha : float, default=1.0 Opacity for the background image. """ # Lazy imports to avoid heavy deps at import-time import cartopy.crs as ccrs import cartopy.feature as cfeature import matplotlib.pyplot as plt if extent is not None: try: ax.set_extent(extent, crs=ccrs.PlateCarree()) except Exception: pass if image_path: try: img = plt.imread(image_path) ax.imshow( img, origin="upper", extent=extent or [-180, 180, -90, 90], transform=ccrs.PlateCarree(), alpha=alpha, ) except Exception: # Swallow image read failures; caller may still draw data pass if features: for feat in features: f = (feat or "").lower() if f == "coastline": ax.add_feature(cfeature.COASTLINE, linewidth=0.5, edgecolor="#333333CC") elif f == "borders": ax.add_feature(cfeature.BORDERS, linewidth=0.5, edgecolor="#33333380") elif f == "gridlines": try: gl = ax.gridlines( draw_labels=False, linewidth=0.2, color="#00000033" ) gl.xlocator = None # let Cartopy choose gl.ylocator = None except Exception: pass
[docs] def add_basemap_tile( ax, extent: Optional[Iterable[float]] = None, *, tile_source: str | None = None, zoom: int = 3, ): """Add a tile basemap using contextily, if available. Notes ----- - This is a best-effort helper. If ``contextily`` is not installed or tiles cannot be fetched (e.g., no network), the function returns without raising. - The axis is expected to use PlateCarree. """ try: import cartopy.crs as ccrs import contextily as cx # type: ignore except Exception: return # graceful no-op if extent is not None: try: ax.set_extent(extent, crs=ccrs.PlateCarree()) except Exception: pass try: # contextily expects Web Mercator typically; let it handle reprojection. # We draw into the Matplotlib axis backing the Cartopy GeoAxes. if tile_source is None: cx.add_basemap(ax, zoom=zoom) else: cx.add_basemap(ax, source=tile_source, zoom=zoom) except Exception: # Network or provider errors are ignored. return