-
Notifications
You must be signed in to change notification settings - Fork 523
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add a rio create command. Resolves #3021. * Eliminate deprecated pytest.warns usage * Add CLI doc for rio create * Fix punctuation. * Add bounds option for geotransform, remove duplicate nodata option. * Warn about duplicate georeferencing
- Loading branch information
Showing
9 changed files
with
756 additions
and
74 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
"""The rio create command.""" | ||
|
||
import click | ||
import json | ||
import os | ||
|
||
import rasterio | ||
from rasterio.crs import CRS | ||
from rasterio.errors import ( | ||
CRSError, | ||
FileOverwriteError, | ||
RasterioIOError, | ||
) | ||
from rasterio.rio import options | ||
from rasterio.transform import Affine, guard_transform | ||
|
||
|
||
def crs_handler(ctx, param, value): | ||
"""Get crs value from the command line.""" | ||
retval = None | ||
if value is not None: | ||
try: | ||
retval = CRS.from_string(value) | ||
except CRSError: | ||
raise click.BadParameter( | ||
f"{value} is not a recognized CRS.", param=param, param_hint="crs" | ||
) | ||
return retval | ||
|
||
|
||
def transform_handler(ctx, param, value): | ||
"""Get transform value from the command line.""" | ||
retval = None | ||
if value is not None: | ||
try: | ||
value = json.loads(value) | ||
retval = guard_transform(value) | ||
except Exception: | ||
raise click.BadParameter( | ||
f"{value} is not recognized as a transformarray.", | ||
param=param, | ||
param_hint="transform", | ||
) | ||
return retval | ||
|
||
|
||
@click.command(short_help="Create an empty or filled dataset.") | ||
@options.file_out_arg | ||
@options.format_opt | ||
@options.dtype_opt | ||
@click.option("--count", "-n", type=int, help="Number of raster bands.") | ||
@click.option("--height", "-h", type=int, help="Raster height, or number of rows.") | ||
@click.option("--width", "-w", type=int, help="Raster width, or number of columns.") | ||
@options.nodata_opt | ||
@click.option( | ||
"--crs", callback=crs_handler, default=None, help="Coordinate reference system." | ||
) | ||
@click.option( | ||
"--transform", | ||
callback=transform_handler, | ||
help="Affine transform matrix. Overrides any given bounds option.", | ||
) | ||
@options.bounds_opt | ||
@options.overwrite_opt | ||
@options.creation_options | ||
@click.pass_context | ||
def create( | ||
ctx, | ||
output, | ||
driver, | ||
dtype, | ||
count, | ||
height, | ||
width, | ||
nodata, | ||
crs, | ||
transform, | ||
bounds, | ||
overwrite, | ||
creation_options, | ||
): | ||
"""Create an empty dataset. | ||
The fundamental, required parameters are: format driver name, data | ||
type, count of bands, height and width in pixels. Long and short | ||
options are provided for each of these. Coordinate reference system | ||
and affine transformation matrix are not strictly required and have | ||
long options only. All other format specific creation outputs must | ||
be specified using the --co option. | ||
Simple north-up, non-rotated georeferencing can be set by using the | ||
--bounds option. The --transform option will assign an arbitrarily | ||
rotated affine transformation matrix to the dataset. Ground control | ||
points, rational polynomial coefficients, and geolocation matrices | ||
are not supported. | ||
The pixel values of an empty dataset are format specific. "Smart" | ||
formats like GTiff use 0 or the nodata value if provided. | ||
Example: | ||
\b | ||
$ rio create new.tif -f GTiff -t uint8 -n 3 -h 512 -w 512 \\ | ||
> --co tiled=true --co blockxsize=256 --co blockysize=256 | ||
The command above produces a 3-band GeoTIFF with 256 x 256 internal | ||
tiling. | ||
""" | ||
# Preventing rio create from overwriting local and remote files, | ||
# objects, and datasets is complicated. | ||
if os.path.exists(output): | ||
if not overwrite: | ||
raise FileOverwriteError( | ||
"File exists and won't be overwritten without use of the '--overwrite' option." | ||
) | ||
else: # Check remote or other non-file output. | ||
try: | ||
with rasterio.open(output) as dataset: | ||
# Dataset exists. May or may not be overwritten. | ||
if not overwrite: | ||
raise FileOverwriteError( | ||
"Dataset exists and won't be overwritten without use of the '--overwrite' option." | ||
) | ||
except RasterioIOError as exc: | ||
# TODO: raise a different exception from rasterio.open() in | ||
# this case? | ||
if "No such file or directory" in str(exc): | ||
pass # Good, output does not exist. Continue with no error. | ||
else: | ||
# Remote output exists, but is not a rasterio dataset. | ||
if not overwrite: | ||
raise FileOverwriteError( | ||
"Object exists and won't be overwritten without use of the '--overwrite' option." | ||
) | ||
|
||
# Prepare the dataset's georeferencing. | ||
geo_transform = None | ||
|
||
if bounds: | ||
left, bottom, right, top = bounds | ||
sx = (right - left) / width | ||
sy = (bottom - top) / height | ||
geo_transform = Affine.translation(left, top) * Affine.scale(sx, sy) | ||
if transform: | ||
if geo_transform is not None: | ||
click.echo( | ||
"--transform value is overriding --bounds value. " | ||
"Use only one of these options to avoid this warning.", | ||
err=True, | ||
) | ||
geo_transform = transform | ||
|
||
profile = dict( | ||
driver=driver, | ||
dtype=dtype, | ||
count=count, | ||
height=height, | ||
width=width, | ||
nodata=nodata, | ||
crs=crs, | ||
transform=geo_transform, | ||
**creation_options, | ||
) | ||
|
||
with ctx.obj["env"], rasterio.open(output, "w", **profile) as dataset: | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.