# coding: utf-8 """Manage overviews of a dataset.""" from functools import reduce import logging import operator import click from . import options import rasterio from rasterio.enums import _OverviewResampling as OverviewResampling def build_handler(ctx, param, value): if value: try: if '^' in value: base, exp_range = value.split('^') exp_min, exp_max = (int(v) for v in exp_range.split('..')) value = [pow(int(base), k) for k in range(exp_min, exp_max + 1)] elif ',' in value: value = [int(v) for v in value.split(',')] elif value == "auto": pass else: raise Exception except Exception: raise click.BadParameter(u"must match 'n,n,n,…', 'n^n..n', or 'auto'.") return value def get_maximum_overview_level(width, height, minsize=256): """ Calculate the maximum overview level of a dataset at which the smallest overview is smaller than `minsize`. Attributes ---------- width : int Width of the dataset. height : int Height of the dataset. minsize : int (default: 256) Minimum overview size. Returns ------- overview_level: int overview level. """ overview_level = 0 overview_factor = 1 while min(width // overview_factor, height // overview_factor) > minsize: overview_factor *= 2 overview_level += 1 return overview_level @click.command('overview', short_help="Construct overviews in an existing dataset.") @options.file_in_arg @click.option('--build', callback=build_handler, metavar=u"f1,f2,…|b^min..max|auto", help="A sequence of decimation factors specified as " "comma-separated list of numbers or a base and range of " "exponents, or 'auto' to automatically determine the maximum factor.") @click.option('--ls', help="Print the overviews for each band.", is_flag=True, default=False) @click.option('--rebuild', help="Reconstruct existing overviews.", is_flag=True, default=False) @click.option('--resampling', help="Resampling algorithm.", type=click.Choice([it.name for it in OverviewResampling]), default='nearest', show_default=True) @click.pass_context def overview(ctx, input, build, ls, rebuild, resampling): """Construct overviews in an existing dataset. A pyramid of overviews computed once and stored in the dataset can improve performance in some applications. The decimation levels at which to build overviews can be specified as a comma separated list rio overview --build 2,4,8,16 or a base and range of exponents rio overview --build 2^1..4 or 'auto' to automatically determine the maximum decimation level at which the smallest overview is smaller than 256 pixels in size. rio overview --build auto Note that overviews can not currently be removed and are not automatically updated when the dataset's primary bands are modified. Information about existing overviews can be printed using the --ls option. rio overview --ls """ with ctx.obj['env']: if ls: with rasterio.open(input, 'r') as dst: resampling_method = dst.tags( ns='rio_overview').get('resampling') or 'unknown' click.echo("Overview factors:") for idx in dst.indexes: click.echo(" Band %d: %s (method: '%s')" % ( idx, dst.overviews(idx) or 'None', resampling_method)) elif rebuild: with rasterio.open(input, 'r+') as dst: # Build the same overviews for all bands. factors = reduce( operator.or_, [set(dst.overviews(i)) for i in dst.indexes]) # Attempt to recover the resampling method from dataset tags. resampling_method = dst.tags( ns='rio_overview').get('resampling') or resampling dst.build_overviews( list(factors), OverviewResampling[resampling_method]) elif build: with rasterio.open(input, 'r+') as dst: if build == "auto": overview_level = get_maximum_overview_level(dst.width, dst.height) build = [2 ** j for j in range(1, overview_level + 1)] dst.build_overviews(build, OverviewResampling[resampling]) # Save the resampling method to a tag. dst.update_tags(ns='rio_overview', resampling=resampling) else: raise click.UsageError( "Please specify --ls, --rebuild, or --build ...")