Repository: rasterio/rasterio

Issue:
rio warp like silently ignores res override
The obvious starting place is `rio warp --like` but this doesn't allow you to override the resolution. It silently ignores the `--res` option which could be considered a bug. 

```
$ rio warp --like b.tif --res 5  a.tif c.tif
$ rio info --res c.tif
1.0 1.0
```

In this case, warp should either a) override the resolution of the like raster or b) raise an exception saying that it's not supported



Discussion:
@perrygeo the help message makes this pretty clear:

```
If a template raster is provided using the --like option, the coordinate
  reference system, affine transform, and dimensions of that raster will be
  used for the output.  In this case --dst-crs, --bounds, --res, and
  --dimensions options are ignored.
```

But an exception for incompatible combinations of parameters would make it yet more obvious.   Do you indeed want something that strong?

I'm 👎  on overriding the resolution of the `--like` raster (defeats part of the point of using that, and begets more complexity in the logic), but willing to be convinced otherwise depending on the use case.

Matching the crs and bounds but not res is a common use case in a recent project here, but can easily be accomplished in gdalwarp or with rio warp once #815 is resolved.

Agreed that `--like` should not allow overrides, so we should enforce it by raising `click.BadParameter`


Resolving patch:
diff --git a/rasterio/rio/warp.py b/rasterio/rio/warp.py
--- a/rasterio/rio/warp.py
+++ b/rasterio/rio/warp.py
@@ -66,7 +66,8 @@ def warp(ctx, files, output, driver, like, dst_crs, dimensions, src_bounds,
     If a template raster is provided using the --like option, the
     coordinate reference system, affine transform, and dimensions of
     that raster will be used for the output.  In this case --dst-crs,
-    --bounds, --res, and --dimensions options are ignored.
+    --bounds, --res, and --dimensions options are not applicable and
+    an exception will be raised.
 
     \b
         $ rio warp input.tif output.tif --like template.tif
@@ -83,7 +84,8 @@ def warp(ctx, files, output, driver, like, dst_crs, dimensions, src_bounds,
     \b
         --dst-crs '{"proj": "utm", "zone": 18, ...}'
 
-    If --dimensions are provided, --res and --bounds are ignored.
+    If --dimensions are provided, --res and --bounds are not applicable and an
+    exception will be raised.
     Resolution is calculated based on the relationship between the
     raster bounds in the target coordinate system and the dimensions,
     and may produce rectangular rather than square pixels.
@@ -114,6 +116,20 @@ def warp(ctx, files, output, driver, like, dst_crs, dimensions, src_bounds,
         # Expand one value to two if needed
         res = (res[0], res[0]) if len(res) == 1 else res
 
+    # Check invalid parameter combinations
+    if like:
+        invalid_combos = (dimensions, dst_bounds, dst_crs, res)
+        if any(p for p in invalid_combos if p is not None):
+            raise click.BadParameter(
+                "--like cannot be used with any of --dimensions, --bounds, "
+                "--dst-crs, or --res")
+
+    elif dimensions:
+        invalid_combos = (dst_bounds, res)
+        if any(p for p in invalid_combos if p is not None):
+            raise click.BadParameter(
+                "--dimensions cannot be used with --bounds or --res")
+
     with rasterio.Env(CPL_DEBUG=verbosity > 2,
                       CHECK_WITH_INVERT_PROJ=check_invert_proj):
         with rasterio.open(files[0]) as src:


Test patch:
diff --git a/tests/test_rio_warp.py b/tests/test_rio_warp.py
--- a/tests/test_rio_warp.py
+++ b/tests/test_rio_warp.py
@@ -278,6 +278,24 @@ def test_warp_reproject_dimensions(runner, tmpdir):
                                   [output.affine.a, -output.affine.e])
 
 
+def test_warp_reproject_dimensions_invalid_params(runner, tmpdir):
+    srcname = 'tests/data/shade.tif'
+    outputname = str(tmpdir.join('test.tif'))
+    bad_params = [
+        ['--bounds', '0', '0', '10', '10'],
+        ['--res', '10']
+    ]
+
+    for param in bad_params:
+        result = runner.invoke(warp.warp,
+                               [srcname, outputname, '--dst-crs', 'EPSG:4326',
+                                '--dimensions', '100', '100'] +
+                               param)
+
+        assert result.exit_code == 2
+        assert '--dimensions cannot be used with' in result.output
+
+
 def test_warp_reproject_bounds_no_res(runner, tmpdir):
     srcname = 'tests/data/shade.tif'
     outputname = str(tmpdir.join('test.tif'))
@@ -394,6 +412,25 @@ def test_warp_reproject_like(runner, tmpdir):
         assert output.height == 10
 
 
+def test_warp_reproject_like_invalid_params(runner, tmpdir):
+    srcname = 'tests/data/shade.tif'
+    outputname = str(tmpdir.join('test.tif'))
+    bad_params = [
+        ['--dimensions', '10', '10'],
+        ['--dst-crs', 'EPSG:4326'],
+        ['--bounds', '0', '0', '10', '10'],
+        ['--res', '10']
+    ]
+
+    for param in bad_params:
+        result = runner.invoke(warp.warp,
+                               [srcname, outputname, '--like', srcname] +
+                               param)
+
+        assert result.exit_code == 2
+        assert '--like cannot be used with any of' in result.output
+
+
 def test_warp_reproject_nolostdata(runner, tmpdir):
     srcname = 'tests/data/world.byte.tif'
     outputname = str(tmpdir.join('test.tif'))