Here’s how you can use tiles offline.
- First cache/convert the tiles.
from pathlib import Path
import numpy as np
from shapely import box
import cartopy.io.img_tiles as cimgt
import cartopy.crs as ccrs
from PIL import Image
def cache_tiles(
tile_source,
max_target_z=1,
x_bounds=(-180, 180),
y_bounds=(-90, 90),
cache_dir="tiles",
):
"""
Caches map tiles within specified bounds from a given tile source.
Args:
tile_source (str or cartopy.io.img_tiles.Tiles): The tile source to use for caching.
It can be a string specifying a built-in tile source, or an instance of a custom tile source class.
max_target_z (int, optional): The maximum zoom level to cache. Defaults to 1.
x_bounds (tuple, optional): The longitudinal bounds of the tiles to cache. Defaults to (-180, 180).
y_bounds (tuple, optional): The latitudinal bounds of the tiles to cache. Defaults to (-90, 90).
cache_dir (str, optional): The directory to store the cached tiles. Defaults to "tiles".
Returns:
pathlib.Path: The path to the cache directory.
"""
if not isinstance(tile_source, cimgt.GoogleWTS):
tile_source = getattr(cimgt, tile_source)
tiles = tile_source(cache=cache_dir)
bbox = ccrs.GOOGLE_MERCATOR.transform_points(
ccrs.PlateCarree(), x=np.array(x_bounds), y=np.array(y_bounds)
)[
:, :-1
].flatten() # drop Z, then convert to x0, y0, x1, y1
target_domain = box(*bbox)
for target_z in range(max_target_z):
tiles.image_for_domain(target_domain, target_z)
return Path(cache_dir) / tile_source.__name__
def convert_tiles_cache(cache_dir):
"""
Converts cached tiles from numpy format to PNG format.
Args:
cache_dir (str): The directory containing the cached tiles in numpy format.
Returns:
str: The format string representing the converted PNG tiles.
"""
for np_path in Path(cache_dir).rglob("*.npy"):
img = Image.fromarray(np.load(np_path))
img_path = Path(str(np_path.with_suffix(".png")).replace("_", "/"))
img_path.parent.mkdir(parents=True, exist_ok=True)
img.save(img_path)
tiles_fmt = str(cache_dir / "{X}" / "{Y}" / "{Z}.png")
return tiles_fmt
tiles_dir = convert_tiles_cache(cache_tiles("OSM", max_target_z=6))
Here’s what the directory looks like:
- Then using it in GeoViews is effortless!
import geoviews as gv
gv.extension("bokeh")
gv.WMTS(tiles_dir).opts(global_extent=True)
At various zoom levels:
At deep zoom levels, it’ll result in white because there’s no data at that zoom level.
To prevent it from trying to access missing data, set max_zoom
equal to your max_target_z
import geoviews as gv
gv.extension("bokeh")
gv.WMTS(tiles_dir).opts(global_extent=True, max_zoom=6)