Spaces:
Configuration error
Configuration error
import os | |
import hashlib | |
import tempfile | |
import contextily as ctx | |
from mpl_toolkits.basemap import Basemap | |
import cartopy.crs as ccrs | |
import cartopy.feature as cfeature | |
from PIL import Image | |
import matplotlib.pyplot as plt | |
# Determine platform and fallback cache path | |
def get_cache_dir(app_name): | |
if os.name == 'nt': | |
return os.path.join(os.getenv('LOCALAPPDATA', tempfile.gettempdir()), f"{app_name}_cache") | |
elif os.name == 'posix': | |
home_dir = os.path.expanduser("~") | |
if os.path.isdir(home_dir) and os.access(home_dir, os.W_OK): | |
return os.path.join(home_dir, f".{app_name}_cache") | |
else: | |
return os.path.join(tempfile.gettempdir(), f"{app_name}_cache") | |
else: | |
return os.path.join(tempfile.gettempdir(), f"{app_name}_cache") | |
# Define cache directories | |
CTX_TILE_CACHE_DIR = get_cache_dir("contextily") | |
BASEMAP_TILE_CACHE_DIR = get_cache_dir("basemap") | |
os.environ["XDG_CACHE_HOME"] = CTX_TILE_CACHE_DIR | |
os.makedirs(CTX_TILE_CACHE_DIR, exist_ok=True) | |
os.makedirs(BASEMAP_TILE_CACHE_DIR, exist_ok=True) | |
def draw_etopo_basemap(ax, mode="basemap", zoom=11): | |
""" | |
Draws a high-resolution basemap background on the provided Cartopy GeoAxes. | |
Parameters | |
---------- | |
ax : matplotlib.axes._subplots.AxesSubplot | |
The matplotlib Axes object (with Cartopy projection) to draw the map background on. | |
mode : str, optional | |
The basemap mode to use: | |
- "stock": Default stock image from Cartopy. | |
- "contextily": Web tile background (CartoDB Voyager), with caching. | |
- "basemap": High-resolution shaded relief using Basemap, with caching. | |
Default is "basemap". | |
zoom : int, optional | |
Tile zoom level (only for "contextily"). Higher = more detail. Default is 7. | |
Notes | |
----- | |
- Uses high resolution for Basemap (resolution='h') and saves figure at 300 DPI. | |
- Cached images are reused using extent-based hashing to avoid re-rendering. | |
- Basemap is deprecated; Cartopy with web tiles is recommended for new projects. | |
""" | |
try: | |
if mode == "stock": | |
ax.stock_img() | |
elif mode == "contextily": | |
extent = ax.get_extent(crs=ccrs.PlateCarree()) | |
ax.set_extent(extent, crs=ccrs.PlateCarree()) | |
ctx.add_basemap( | |
ax, | |
crs=ccrs.PlateCarree(), | |
source=ctx.providers.CartoDB.Voyager, | |
zoom=zoom | |
) | |
elif mode == "basemap": | |
extent = ax.get_extent(crs=ccrs.PlateCarree()) | |
extent_str = f"{extent[0]:.4f}_{extent[1]:.4f}_{extent[2]:.4f}_{extent[3]:.4f}" | |
cache_key = hashlib.md5(extent_str.encode()).hexdigest() | |
cache_file = os.path.join(BASEMAP_TILE_CACHE_DIR, f"{cache_key}_highres.png") | |
if os.path.exists(cache_file): | |
img = Image.open(cache_file) | |
ax.imshow(img, extent=extent, transform=ccrs.PlateCarree()) | |
else: | |
temp_fig, temp_ax = plt.subplots(figsize=(12, 9), | |
subplot_kw={'projection': ccrs.PlateCarree()}) | |
temp_ax.set_extent(extent, crs=ccrs.PlateCarree()) | |
m = Basemap(projection='cyl', | |
llcrnrlon=extent[0], urcrnrlon=extent[1], | |
llcrnrlat=extent[2], urcrnrlat=extent[3], | |
resolution='f', ax=temp_ax) | |
m.shadedrelief() | |
temp_fig.savefig(cache_file, dpi=300, bbox_inches='tight', pad_inches=0) | |
plt.close(temp_fig) | |
img = Image.open(cache_file) | |
ax.imshow(img, extent=extent, transform=ccrs.PlateCarree()) | |
else: | |
raise ValueError(f"Unsupported basemap mode: {mode}") | |
except Exception as e: | |
print(f"[Relief Error - {mode} mode]:", e) | |
ax.add_feature(cfeature.LAND) | |
ax.add_feature(cfeature.OCEAN) | |