Fast, scriptable map tile downloader and mosaicker for geospatial workflows.
tilegrab downloads raster map tiles from common providers (OSM, Google Satellite, ESRI World Imagery) using a vector extent (polygon shape or bounding box), then optionally mosaics them into a single or multiple rasters.
Most tile downloaders have two major drawbacks:
- GUI tools that don’t scale or automate
- Scripts that only support bounding boxes and break on real geometries
tilegrab is different:
- Uses actual vector geometries, not only just extents
- Scalable API
- Clean CLI, easy to script and integrate
- Works with Shapefiles, GeoPackages, GeoJSON
- Supports download-only, mosaic-only or full pipelines
- Designed for GIS, remote sensing, and map production workflows
- Vector-driven tile selection
- Exact geometry-based tile filtering
- Or fast bounding-box-based selection
- Multiple tile providers
- OpenStreetMap
- Google Satellite
- ESRI World Imagery
- or Custom providers
- Tile mosaicking
- Progress reporting (optional)
- API-key support where required
- Sensible defaults, strict CLI validation
pip install -i tilegrab pip install tilegrab==1.2.0b2tilegrab \
--source boundary.shp \
--shape \
--osm \
--zoom 16tilegrab \
--source boundary.geojson \
--bbox \
--esri_sat \
--zoom 17usage: tilegrab [-h] --source SOURCE (--shape | --bbox) (--osm | --google_sat | --esri_sat | --key KEY) (--jpg | --png | --tiff) --zoom ZOOM [--tiles-out TILES_OUT] [--download-only] [--mosaic-only]
[--group-tiles GROUP_TILES] [--group-overlap] [--tile-limit TILE_LIMIT] [--workers WORKERS] [--no-parallel] [--no-progress] [--quiet] [--debug]
Download and mosaic map tiles
options:
-h, --help show this help message and exit
--zoom ZOOM Zoom level (integer between 1 and 22)
--tiles-out TILES_OUT
Output directory for downloaded tiles (default: ./saved_tiles)
--download-only Only download tiles; do not run mosaicking or postprocessing
--mosaic-only Only mosaic tiles; do not download
--group-tiles GROUP_TILES
Mosaic tiles but according to given WxH into ./grouped_tiles
--group-overlap Overlap with the next consecutive tile when grouping
--tile-limit TILE_LIMIT
Override maximum tile limit that can download (use with caution)
--workers WORKERS Max number of threads to use when parallel downloading
--no-parallel Download tiles sequentially, no parallel downloading
--no-progress Hide tile download progress bar
--quiet Hide all prints
--debug Enable debug logging
Source options(Extent):
Options for the vector polygon source
--source SOURCE The vector polygon source for filter tiles
--shape Use actual shape to derive tiles
--bbox Use shape's bbox to derive tiles
Source options(Map tiles):
Options for the map tile source
--osm OpenStreetMap
--google_sat Google Satellite
--esri_sat ESRI World Imagery
--key KEY API key where required by source
Mosaic export formats:
Formats for the output mosaic image
--jpg JPG image; no geo-reference
--png PNG image; no geo-reference
--tiff GeoTiff image; with geo-reference
Any format readable by GeoPandas, including:
- Shapefile (
.shp) - GeoPackage (
.gpkg) - GeoJSON (
.geojson) - Spatial databases (via supported drivers)
tilegrab is not limited to built-in providers.
If a tile service follows the standard {z}/{x}/{y} pattern, you can add it in one small class by extending TileSource.
from tilegrab.sources import TileSource
class MyCustomSource(TileSource):
name = "MyCustomSource name"
description = "MyCustomSource description"
url_template = "https://MyCustomSource/{z}/{x}/{y}.png"You can change how the url is generate by override get_url function, inside your Custom Tile Sources. If you are planning to use API key, you must override this function.
def get_url(self, z: int, x: int, y: int) -> str:
assert self.api_key
return self.url_template.format(x=x, y=y, z=z, token=self.api_key)Your tile source must define:
url_templateMust contain{z},{x},{y}placeholders.
Optional but recommended:
name– Human-readable namedescription– Short description of the imagery
If your provider requires an API key, pass it during instantiation:
source = MyCustomSource(api_key="YOUR_KEY")Custom sources are intended for programmatic use (not CLI flags):
from tilegrab.downloader import Downloader
from tilegrab.tiles import TilesByShape
from tilegrab.dataset import GeoDataset
dataset = GeoDataset("area.gpkg")
tile_collection = TilesByShape(dataset, zoom=16)
tile_source = MyCustomSource(api_key="XYZ")
downloader = Downloader(tile_collection, tile_source, "output")
downloader.run()This keeps the CLI clean while giving developers full control.
- Zero configuration overhead
- No registry or plugin boilerplate
- Easy to vendor in private or internal tile servers
- Safe default for public CLI usage
If you need full flexibility, use the Python API.
Planned (not promises):
- Additional tile providers
- Parallel download tuning
- Raster reprojection and resampling options
- Expanded Python API documentation
- Test implementation
MIT License. Do whatever you want — just don’t pretend you wrote it.
Thiwanka Munasinghe GitHub: https://github.com/thiwaK