Add param error handling.

Surface the geographic and projected crs testing functions to the
crs module and use them within edit-info. Merge the edit-info cmd
into the info module alongside the info cmd. Add a few more tests
of the error handling to get to 100% coverage of the info cmd
module.
This commit is contained in:
Sean Gillies 2015-05-20 14:47:46 -06:00
parent 3a783277f7
commit 9fed2a005b
5 changed files with 120 additions and 68 deletions

View File

@ -10,6 +10,7 @@
# {'proj': 'longlat', 'ellps': 'WGS84', 'datum': 'WGS84', 'no_defs': True}
#
from rasterio._base import is_geographic_crs, is_projected_crs
from rasterio.five import string_types
def to_string(crs):

View File

@ -1,65 +0,0 @@
"""Edit raster dataset metadata from the command line."""
import json
import logging
import click
import rasterio
from rasterio.rio.cli import cli, file_in_arg
from rasterio.transform import guard_transform
@cli.command(short_help="Edit dataset metadata.")
@file_in_arg
@click.option('--nodata', type=float, default=None,
help="New nodata value")
@click.option('--crs', help="New coordinate reference system")
@click.option('--transform', help="New affine transform matrix")
@click.option('--tag', 'tags', multiple=True, metavar='KEY=VAL',
help="New tag.")
@click.pass_context
def edit(ctx, input, nodata, crs, transform, tags):
"""Edit a dataset's metadata: coordinate reference system, affine
transformation matrix, nodata value, and tags.
CRS may be either a PROJ.4 or EPSG:nnnn string, or a JSON-encoded
PROJ.4 object.
Transforms are either JSON-encoded Affine objects (preferred) like
[300.038, 0.0, 101985.0, 0.0, -300.042, 2826915.0]
or JSON-encoded GDAL geotransform arrays like
[101985.0, 300.038, 0.0, 2826915.0, 0.0, -300.042]
"""
verbosity = (ctx.obj and ctx.obj.get('verbosity')) or 1
logger = logging.getLogger('rio')
with rasterio.drivers(CPL_DEBUG=(verbosity > 2)) as env:
with rasterio.open(input, 'r+') as dst:
# Update nodata.
if nodata:
dst.nodata = nodata
# Update CRS. Value might be a PROJ.4 string or a JSON
# encoded dict.
if crs:
crs = crs.strip()
try:
crs = json.loads(crs)
except ValueError:
pass
dst.crs = crs
# Update transform. Value might be a JSON encoded
# Affine object or a GDAL geotransform array.
if transform:
dst.transform = guard_transform(json.loads(transform))
# Update tags.
if tags:
tags = dict(p.split('=') for p in tags)
dst.update_tags(**tags)

View File

@ -1,4 +1,4 @@
# Info command.
"""Fetch and edit raster dataset metadata from the command line."""
import json
import logging
@ -9,6 +9,94 @@ import click
import rasterio
import rasterio.crs
from rasterio.rio.cli import cli, bidx_opt, file_in_arg, masked_opt
from rasterio.transform import guard_transform
@cli.command('edit-info', short_help="Edit dataset metadata.")
@file_in_arg
@click.option('--nodata', type=float, default=None,
help="New nodata value")
@click.option('--crs', help="New coordinate reference system")
@click.option('--transform', help="New affine transform matrix")
@click.option('--tag', 'tags', multiple=True, metavar='KEY=VAL',
help="New tag.")
@click.pass_context
def edit(ctx, input, nodata, crs, transform, tags):
"""Edit a dataset's metadata: coordinate reference system, affine
transformation matrix, nodata value, and tags.
CRS may be either a PROJ.4 or EPSG:nnnn string, or a JSON-encoded
PROJ.4 object.
Transforms are either JSON-encoded Affine objects (preferred) like
[300.038, 0.0, 101985.0, 0.0, -300.042, 2826915.0]
or JSON-encoded GDAL geotransform arrays like
[101985.0, 300.038, 0.0, 2826915.0, 0.0, -300.042]
"""
verbosity = (ctx.obj and ctx.obj.get('verbosity')) or 1
logger = logging.getLogger('rio')
with rasterio.drivers(CPL_DEBUG=(verbosity > 2)) as env:
with rasterio.open(input, 'r+') as dst:
# Update nodata.
if nodata is not None:
from rasterio._io import in_dtype_range
dtype = dst.dtypes[0]
if not in_dtype_range(nodata, dtype):
raise click.BadParameter(
"outside the range of the file's "
"data type (%s)." % dtype,
param=nodata, param_hint='nodata')
dst.nodata = nodata
# Update CRS. Value might be a PROJ.4 string or a JSON
# encoded dict.
if crs:
new_crs = crs.strip()
try:
new_crs = json.loads(crs)
except ValueError:
pass
if not (rasterio.crs.is_geographic_crs(new_crs) or
rasterio.crs.is_projected_crs(new_crs)):
raise click.BadParameter(
"'%s' is not a recognized CRS." % crs,
param=crs, param_hint='crs')
dst.crs = new_crs
# Update transform. Value might be a JSON encoded
# Affine object or a GDAL geotransform array.
if transform:
try:
transform_obj = json.loads(transform)
except ValueError:
raise click.BadParameter(
"'%s' is not a JSON array." % transform,
param=transform, param_hint='transform')
try:
transform_obj = guard_transform(transform_obj)
except:
raise click.BadParameter(
"'%s' is not recognized as an Affine or GDAL "
"geotransform array." % transform,
param=transform, param_hint='transform')
dst.transform = transform_obj
# Update tags.
if tags:
tags = dict(p.split('=') for p in tags)
dst.update_tags(**tags)
@cli.command(short_help="Print information about the rio environment.")

View File

@ -207,7 +207,7 @@ setup_args = dict(
[rasterio.rio_commands]
bounds=rasterio.rio.rio:bounds
calc=rasterio.rio.calc:calc
edit=rasterio.rio.edit:edit
edit-info=rasterio.rio.info:edit
env=rasterio.rio.info:env
info=rasterio.rio.info:info
insp=rasterio.rio.rio:insp

View File

@ -5,12 +5,19 @@ import sys
from click.testing import CliRunner
import rasterio
from rasterio.rio.edit import edit
from rasterio.rio.info import edit
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
def test_edit_nodata_err(data):
runner = CliRunner()
inputfile = str(data.join('RGB.byte.tif'))
result = runner.invoke(edit, [inputfile, '--nodata', '-1'])
assert result.exit_code == 2
def test_edit_nodata(data):
runner = CliRunner()
inputfile = str(data.join('RGB.byte.tif'))
@ -20,6 +27,13 @@ def test_edit_nodata(data):
assert src.nodata == 255.0
def test_edit_crs_err(data):
runner = CliRunner()
inputfile = str(data.join('RGB.byte.tif'))
result = runner.invoke(edit, [inputfile, '--crs', 'LOL:WUT'])
assert result.exit_code == 2
def test_edit_crs_epsg(data):
runner = CliRunner()
inputfile = str(data.join('RGB.byte.tif'))
@ -48,6 +62,20 @@ def test_edit_crs_obj(data):
assert src.crs == {'init': 'epsg:32618'}
def test_edit_transform_err_not_json(data):
runner = CliRunner()
inputfile = str(data.join('RGB.byte.tif'))
result = runner.invoke(edit, [inputfile, '--transform', 'LOL'])
assert result.exit_code == 2
def test_edit_transform_err_bad_array(data):
runner = CliRunner()
inputfile = str(data.join('RGB.byte.tif'))
result = runner.invoke(edit, [inputfile, '--transform', '[1,2]'])
assert result.exit_code == 2
def test_edit_transform_affine(data):
runner = CliRunner()
inputfile = str(data.join('RGB.byte.tif'))