File size: 3,992 Bytes
730ca0c 928c17e 291f0ae 730ca0c d3fc047 928c17e d3fc047 2818072 730ca0c 928c17e 730ca0c 2818072 730ca0c 2818072 d3fc047 2818072 d3fc047 2818072 730ca0c 2818072 d3fc047 2818072 928c17e 2818072 db84eaa d3fc047 730ca0c 2818072 d3fc047 2818072 730ca0c 2818072 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
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)
|