mirror of
https://github.com/protomaps/PMTiles.git
synced 2026-02-04 02:41:09 +00:00
Add rio-pmtiles command line tool. [#338] This is derived from the original mapbox/rio-mbtiles implementation, with changes: * output PMTiles only instead of MBTiles. * Python 3.7+ only. * remove --implementation, --image-dump, --append/--overwrite, --covers features. * bump dependency versions. * better progress reporting; add pyroaring. * update README and license texts. * rio-pmtiles v0.0.6 on PyPI
This commit is contained in:
138
python/rio-pmtiles/rio_pmtiles/worker.py
Normal file
138
python/rio-pmtiles/rio_pmtiles/worker.py
Normal file
@@ -0,0 +1,138 @@
|
||||
"""rio-pmtiles processing worker"""
|
||||
|
||||
import logging
|
||||
import warnings
|
||||
|
||||
from rasterio.enums import Resampling
|
||||
from rasterio.io import MemoryFile
|
||||
from rasterio.transform import from_bounds as transform_from_bounds
|
||||
from rasterio.warp import reproject, transform_bounds
|
||||
from rasterio.windows import Window
|
||||
from rasterio.windows import from_bounds as window_from_bounds
|
||||
import mercantile
|
||||
import rasterio
|
||||
|
||||
TILES_CRS = "EPSG:3857"
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def init_worker(
|
||||
path,
|
||||
profile,
|
||||
resampling_method,
|
||||
open_opts=None,
|
||||
warp_opts=None,
|
||||
creation_opts=None,
|
||||
exclude_empties=True,
|
||||
):
|
||||
global base_kwds, filename, resampling, open_options, warp_options, creation_options, exclude_empty_tiles
|
||||
resampling = Resampling[resampling_method]
|
||||
base_kwds = profile.copy()
|
||||
filename = path
|
||||
open_options = open_opts.copy() if open_opts is not None else {}
|
||||
warp_options = warp_opts.copy() if warp_opts is not None else {}
|
||||
creation_options = creation_opts.copy() if creation_opts is not None else {}
|
||||
exclude_empty_tiles = exclude_empties
|
||||
|
||||
|
||||
def process_tile(tile):
|
||||
"""Process a single PMTiles tile
|
||||
|
||||
Parameters
|
||||
----------
|
||||
tile : mercantile.Tile
|
||||
warp_options : Mapping
|
||||
GDAL warp options as keyword arguments.
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
||||
tile : mercantile.Tile
|
||||
The input tile.
|
||||
bytes : bytearray
|
||||
Image bytes corresponding to the tile.
|
||||
|
||||
"""
|
||||
global base_kwds, resampling, filename, open_options, warp_options, creation_options, exclude_empty_tiles
|
||||
|
||||
with rasterio.open(filename, **open_options) as src:
|
||||
|
||||
bbox = mercantile.xy_bounds(tile)
|
||||
|
||||
kwds = base_kwds.copy()
|
||||
kwds.update(**creation_options)
|
||||
kwds["transform"] = transform_from_bounds(
|
||||
bbox.left, bbox.bottom, bbox.right, bbox.top, kwds["width"], kwds["height"]
|
||||
)
|
||||
src_nodata = kwds.pop("src_nodata", None)
|
||||
dst_nodata = kwds.pop("dst_nodata", None)
|
||||
|
||||
src_alpha = None
|
||||
dst_alpha = None
|
||||
bindexes = None
|
||||
|
||||
if kwds["count"] == 4:
|
||||
bindexes = [1, 2, 3]
|
||||
dst_alpha = 4
|
||||
|
||||
if src.count == 4:
|
||||
src_alpha = 4
|
||||
else:
|
||||
kwds["count"] = 4
|
||||
else:
|
||||
bindexes = list(range(1, kwds["count"] + 1))
|
||||
|
||||
warnings.simplefilter("ignore")
|
||||
|
||||
log.info("Reprojecting tile: tile=%r", tile)
|
||||
|
||||
with MemoryFile() as memfile:
|
||||
|
||||
with memfile.open(**kwds) as tmp:
|
||||
|
||||
# determine window of source raster corresponding to the tile
|
||||
# image, with small buffer at edges
|
||||
try:
|
||||
west, south, east, north = transform_bounds(
|
||||
TILES_CRS, src.crs, bbox.left, bbox.bottom, bbox.right, bbox.top
|
||||
)
|
||||
tile_window = window_from_bounds(
|
||||
west, south, east, north, transform=src.transform
|
||||
)
|
||||
adjusted_tile_window = Window(
|
||||
tile_window.col_off - 1,
|
||||
tile_window.row_off - 1,
|
||||
tile_window.width + 2,
|
||||
tile_window.height + 2,
|
||||
)
|
||||
tile_window = adjusted_tile_window.round_offsets().round_shape()
|
||||
|
||||
# if no data in window, skip processing the tile
|
||||
if (
|
||||
exclude_empty_tiles
|
||||
and not src.read_masks(1, window=tile_window).any()
|
||||
):
|
||||
return tile, None
|
||||
|
||||
except ValueError:
|
||||
log.info(
|
||||
"Tile %r will not be skipped, even if empty. This is harmless.",
|
||||
tile,
|
||||
)
|
||||
|
||||
num_threads = int(warp_options.pop("num_threads", 2))
|
||||
|
||||
reproject(
|
||||
rasterio.band(src, bindexes),
|
||||
rasterio.band(tmp, bindexes),
|
||||
src_nodata=src_nodata,
|
||||
dst_nodata=dst_nodata,
|
||||
src_alpha=src_alpha,
|
||||
dst_alpha=dst_alpha,
|
||||
num_threads=num_threads,
|
||||
resampling=resampling,
|
||||
**warp_options
|
||||
)
|
||||
|
||||
return tile, memfile.read()
|
||||
Reference in New Issue
Block a user