We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
rio warp null transformer error with bad proj4
Currently, if you pass a bad projection, you get the following behavior:

```
$ rio warp --dst-crs "+proj=foobar" tests/data/warp_test.tif /tmp/foo.tif
ERROR:GDAL:CPLE_NotSupported in Failed to initialize PROJ.4 with `+proj=foobar +wktext'.
Traceback (most recent call last):
...
  File "/Users/mperry/work/rasterio/rasterio/rio/warp.py", line 198, in warp
    resolution=res)
  File "/Users/mperry/work/rasterio/rasterio/warp.py", line 296, in calculate_default_transform
    left, bottom, right, top)
  File "rasterio/_warp.pyx", line 535, in rasterio._warp._calculate_default_transform (rasterio/_warp.cpp:9551)
    with InMemoryRaster(
  File "rasterio/_warp.pyx", line 542, in rasterio._warp._calculate_default_transform (rasterio/_warp.cpp:9261)
    raise ValueError("NULL transformer")
ValueError: NULL transformer
```

The transformer fails to initialize, which is reasonable considering the invalid proj string. Is there any way to catch that error and report back something more meaningful than "NULL transformer"?


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `rasterio/rio/warp.py`
Content:
```
1 import logging
2 from math import ceil
3 import warnings
4 
5 import click
6 from cligj import files_inout_arg, format_opt
7 
8 from .helpers import resolve_inout
9 from . import options
10 import rasterio
11 from rasterio import crs
12 from rasterio.transform import Affine
13 from rasterio.warp import (reproject, Resampling, calculate_default_transform,
14    transform_bounds)
15 
16 
17 # Improper usage of rio-warp can lead to accidental creation of
18 # extremely large datasets. We'll put a hard limit on the size of
19 # datasets and raise a usage error if the limits are exceeded.
20 MAX_OUTPUT_WIDTH = 100000
21 MAX_OUTPUT_HEIGHT = 100000
22 
23 
24 def bounds_handler(ctx, param, value):
25     """Warn about future usage changes."""
26     if value:
27         click.echo("Future Warning: "
28             "the semantics of the `--bounds` option will change in Rasterio "
29             "version 1.0 from bounds of the source dataset to bounds of the "
30             "destination dataset.", err=True)
31     return value
32 
33 
34 def x_dst_bounds_handler(ctx, param, value):
35     """Warn about future usage changes."""
36     if value:
37         click.echo("Future Warning: "
38             "the `--x-dst-bounds` option will be removed in Rasterio version "
39             "1.0 in favor of `--bounds`.", err=True)
40     return value
41 
42 
43 @click.command(short_help='Warp a raster dataset.')
44 @files_inout_arg
45 @options.output_opt
46 @format_opt
47 @options.like_file_opt
48 @click.option('--dst-crs', default=None,
49               help='Target coordinate reference system.')
50 @options.dimensions_opt
51 @click.option(
52     '--src-bounds',
53     nargs=4, type=float, default=None,
54     help="Determine output extent from source bounds: left bottom right top "
55          "(note: for future backwards compatibility in 1.0).")
56 @click.option(
57     '--x-dst-bounds',
58     nargs=4, type=float, default=None, callback=x_dst_bounds_handler,
59     help="Set output extent from bounding values: left bottom right top "
60          "(note: this option will be removed in 1.0).")
61 @click.option(
62     '--bounds',
63     nargs=4, type=float, default=None, callback=bounds_handler,
64     help="Determine output extent from source bounds: left bottom right top "
65          "(note: the semantics of this option will change to those of "
66          "`--x-dst-bounds` in version 1.0).")
67 @options.resolution_opt
68 @click.option('--resampling', type=click.Choice([r.name for r in Resampling]),
69               default='nearest', help="Resampling method.",
70               show_default=True)
71 @click.option('--threads', type=int, default=1,
72               help='Number of processing threads.')
73 @click.option('--check-invert-proj', type=bool, default=True,
74               help='Constrain output to valid coordinate region in dst-crs')
75 @options.force_overwrite_opt
76 @options.creation_options
77 @click.pass_context
78 def warp(ctx, files, output, driver, like, dst_crs, dimensions, src_bounds,
79          x_dst_bounds, bounds, res, resampling, threads, check_invert_proj,
80          force_overwrite, creation_options):
81     """
82     Warp a raster dataset.
83 
84     If a template raster is provided using the --like option, the
85     coordinate reference system, affine transform, and dimensions of
86     that raster will be used for the output.  In this case --dst-crs,
87     --bounds, --res, and --dimensions options are ignored.
88 
89     \b
90         $ rio warp input.tif output.tif --like template.tif
91 
92     The output coordinate reference system may be either a PROJ.4 or
93     EPSG:nnnn string,
94 
95     \b
96         --dst-crs EPSG:4326
97         --dst-crs '+proj=longlat +ellps=WGS84 +datum=WGS84'
98 
99     or a JSON text-encoded PROJ.4 object.
100 
101     \b
102         --dst-crs '{"proj": "utm", "zone": 18, ...}'
103 
104     If --dimensions are provided, --res and --bounds are ignored.
105     Resolution is calculated based on the relationship between the
106     raster bounds in the target coordinate system and the dimensions,
107     and may produce rectangular rather than square pixels.
108 
109     \b
110         $ rio warp input.tif output.tif --dimensions 100 200 \\
111         > --dst-crs EPSG:4326
112 
113     If --bounds are provided, --res is required if --dst-crs is provided
114     (defaults to source raster resolution otherwise).
115 
116     \b
117         $ rio warp input.tif output.tif \\
118         > --bounds -78 22 -76 24 --res 0.1 --dst-crs EPSG:4326
119 
120     """
121 
122     verbosity = (ctx.obj and ctx.obj.get('verbosity')) or 1
123     logger = logging.getLogger('rio')
124 
125     output, files = resolve_inout(
126         files=files, output=output, force_overwrite=force_overwrite)
127 
128     resampling = Resampling[resampling]  # get integer code for method
129 
130     if not len(res):
131         # Click sets this as an empty tuple if not provided
132         res = None
133     else:
134         # Expand one value to two if needed
135         res = (res[0], res[0]) if len(res) == 1 else res
136 
137     with rasterio.drivers(CPL_DEBUG=verbosity > 2,
138                           CHECK_WITH_INVERT_PROJ=check_invert_proj):
139         with rasterio.open(files[0]) as src:
140             l, b, r, t = src.bounds
141             out_kwargs = src.meta.copy()
142             out_kwargs['driver'] = driver
143 
144             # Sort out the bounds options.
145             src_bounds = bounds or src_bounds
146             dst_bounds = x_dst_bounds
147             if src_bounds and dst_bounds:
148                 raise click.BadParameter(
149                     "Source and destination bounds may not be specified "
150                     "simultaneously.")
151 
152             if like:
153                 with rasterio.open(like) as template_ds:
154                     dst_crs = template_ds.crs
155                     dst_transform = template_ds.affine
156                     dst_height = template_ds.height
157                     dst_width = template_ds.width
158 
159             elif dst_crs:
160                 try:
161                     dst_crs = crs.from_string(dst_crs)
162                 except ValueError:
163                     raise click.BadParameter("invalid crs format.",
164                                              param=dst_crs, param_hint=dst_crs)
165 
166                 if dimensions:
167                     # Calculate resolution appropriate for dimensions
168                     # in target.
169                     dst_width, dst_height = dimensions
170                     xmin, ymin, xmax, ymax = transform_bounds(src.crs, dst_crs,
171                                                               *src.bounds)
172                     dst_transform = Affine(
173                         (xmax - xmin) / float(dst_width),
174                         0, xmin, 0,
175                         (ymin - ymax) / float(dst_height),
176                         ymax
177                     )
178 
179                 elif src_bounds or dst_bounds:
180                     if not res:
181                         raise click.BadParameter(
182                             "Required when using --bounds.",
183                             param='res', param_hint='res')
184 
185                     if src_bounds:
186                         xmin, ymin, xmax, ymax = transform_bounds(
187                             src.crs, dst_crs, *src_bounds)
188                     else:
189                         xmin, ymin, xmax, ymax = dst_bounds
190 
191                     dst_transform = Affine(res[0], 0, xmin, 0, -res[1], ymax)
192                     dst_width = max(int(ceil((xmax - xmin) / res[0])), 1)
193                     dst_height = max(int(ceil((ymax - ymin) / res[1])), 1)
194 
195                 else:
196                     dst_transform, dst_width, dst_height = calculate_default_transform(
197                         src.crs, dst_crs, src.width, src.height, *src.bounds,
198                         resolution=res)
199 
200             elif dimensions:
201                 # Same projection, different dimensions, calculate resolution.
202                 dst_crs = src.crs
203                 dst_width, dst_height = dimensions
204                 dst_transform = Affine(
205                     (r - l) / float(dst_width),
206                     0, l, 0,
207                     (b - t) / float(dst_height),
208                     t
209                 )
210 
211             elif src_bounds or dst_bounds:
212                 # Same projection, different dimensions and possibly
213                 # different resolution.
214                 if not res:
215                     res = (src.affine.a, -src.affine.e)
216 
217                 dst_crs = src.crs
218                 xmin, ymin, xmax, ymax = (src_bounds or dst_bounds)
219                 dst_transform = Affine(res[0], 0, xmin, 0, -res[1], ymax)
220                 dst_width = max(int(ceil((xmax - xmin) / res[0])), 1)
221                 dst_height = max(int(ceil((ymax - ymin) / res[1])), 1)
222 
223             elif res:
224                 # Same projection, different resolution.
225                 dst_crs = src.crs
226                 dst_transform = Affine(res[0], 0, l, 0, -res[1], t)
227                 dst_width = max(int(ceil((r - l) / res[0])), 1)
228                 dst_height = max(int(ceil((t - b) / res[1])), 1)
229 
230             else:
231                 dst_crs = src.crs
232                 dst_transform = src.affine
233                 dst_width = src.width
234                 dst_height = src.height
235 
236             # When the bounds option is misused, extreme values of
237             # destination width and height may result.
238             if (dst_width < 0 or dst_height < 0 or
239                     dst_width > MAX_OUTPUT_WIDTH or
240                     dst_height > MAX_OUTPUT_HEIGHT):
241                 raise click.BadParameter(
242                     "Invalid output dimensions: {0}.".format(
243                         (dst_width, dst_height)))
244 
245             out_kwargs.update({
246                 'crs': dst_crs,
247                 'transform': dst_transform,
248                 'affine': dst_transform,
249                 'width': dst_width,
250                 'height': dst_height
251             })
252 
253             out_kwargs.update(**creation_options)
254 
255             with rasterio.open(output, 'w', **out_kwargs) as dst:
256                 for i in range(1, src.count + 1):
257 
258                     reproject(
259                         source=rasterio.band(src, i),
260                         destination=rasterio.band(dst, i),
261                         src_transform=src.affine,
262                         src_crs=src.crs,
263                         # src_nodata=#TODO
264                         dst_transform=out_kwargs['transform'],
265                         dst_crs=out_kwargs['crs'],
266                         # dst_nodata=#TODO
267                         resampling=resampling,
268                         num_threads=threads)
269
```

Path: `rasterio/errors.py`
Content:
```
1 """A module of errors."""
2 
3 from click import FileError
4 
5 
6 class RasterioIOError(IOError):
7     """A failure to open a dataset using the presently registered drivers."""
8 
9 
10 class DriverRegistrationError(ValueError):
11     """To be raised when, eg, _gdal.GDALGetDriverByName("MEM") returns NULL."""
12 
13 
14 class FileOverwriteError(FileError):
15     """Rasterio's CLI refuses to implicitly clobber output files."""
16 
17     def __init__(self, message):
18         super(FileOverwriteError, self).__init__('', hint=message)
19
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/rasterio/errors.py b/rasterio/errors.py
--- a/rasterio/errors.py
+++ b/rasterio/errors.py
@@ -3,16 +3,22 @@
 from click import FileError
 
 
-class RasterioIOError(IOError):
-    """A failure to open a dataset using the presently registered drivers."""
+class CRSError(ValueError):
+    """Raised when a CRS string or mapping is invalid or cannot serve
+    to define a coordinate transformation."""
 
 
 class DriverRegistrationError(ValueError):
-    """To be raised when, eg, _gdal.GDALGetDriverByName("MEM") returns NULL."""
+    """Raised when a format driver is requested but is not registered."""
 
 
 class FileOverwriteError(FileError):
-    """Rasterio's CLI refuses to implicitly clobber output files."""
+    """Raised when Rasterio's CLI refuses to clobber output files."""
 
     def __init__(self, message):
         super(FileOverwriteError, self).__init__('', hint=message)
+
+
+class RasterioIOError(IOError):
+    """Raised when a dataset cannot be opened using one of the
+    registered format drivers."""
diff --git a/rasterio/rio/warp.py b/rasterio/rio/warp.py
--- a/rasterio/rio/warp.py
+++ b/rasterio/rio/warp.py
@@ -9,6 +9,7 @@
 from . import options
 import rasterio
 from rasterio import crs
+from rasterio.errors import CRSError
 from rasterio.transform import Affine
 from rasterio.warp import (reproject, Resampling, calculate_default_transform,
    transform_bounds)
@@ -159,16 +160,20 @@
             elif dst_crs:
                 try:
                     dst_crs = crs.from_string(dst_crs)
-                except ValueError:
-                    raise click.BadParameter("invalid crs format.",
-                                             param=dst_crs, param_hint=dst_crs)
+                except ValueError as err:
+                    raise click.BadParameter(
+                        str(err), param='dst_crs', param_hint='dst_crs')
 
                 if dimensions:
                     # Calculate resolution appropriate for dimensions
                     # in target.
                     dst_width, dst_height = dimensions
-                    xmin, ymin, xmax, ymax = transform_bounds(src.crs, dst_crs,
-                                                              *src.bounds)
+                    try:
+                        xmin, ymin, xmax, ymax = transform_bounds(
+                            src.crs, dst_crs, *src.bounds)
+                    except CRSError as err:
+                        raise click.BadParameter(
+                            str(err), param='dst_crs', param_hint='dst_crs')
                     dst_transform = Affine(
                         (xmax - xmin) / float(dst_width),
                         0, xmin, 0,
@@ -183,8 +188,13 @@
                             param='res', param_hint='res')
 
                     if src_bounds:
-                        xmin, ymin, xmax, ymax = transform_bounds(
-                            src.crs, dst_crs, *src_bounds)
+                        try:
+                            xmin, ymin, xmax, ymax = transform_bounds(
+                                src.crs, dst_crs, *src_bounds)
+                        except CRSError as err:
+                            raise click.BadParameter(
+                                str(err), param='dst_crs',
+                                param_hint='dst_crs')
                     else:
                         xmin, ymin, xmax, ymax = dst_bounds
 
@@ -193,10 +203,13 @@
                     dst_height = max(int(ceil((ymax - ymin) / res[1])), 1)
 
                 else:
-                    dst_transform, dst_width, dst_height = calculate_default_transform(
-                        src.crs, dst_crs, src.width, src.height, *src.bounds,
-                        resolution=res)
-
+                    try:
+                        dst_transform, dst_width, dst_height = calculate_default_transform(
+                            src.crs, dst_crs, src.width, src.height,
+                            *src.bounds, resolution=res)
+                    except CRSError as err:
+                        raise click.BadParameter(
+                            str(err), param='dst_crs', param_hint='dst_crs')
             elif dimensions:
                 # Same projection, different dimensions, calculate resolution.
                 dst_crs = src.crs

{"golden_diff": "diff --git a/rasterio/errors.py b/rasterio/errors.py\n--- a/rasterio/errors.py\n+++ b/rasterio/errors.py\n@@ -3,16 +3,22 @@\n from click import FileError\n \n \n-class RasterioIOError(IOError):\n-    \"\"\"A failure to open a dataset using the presently registered drivers.\"\"\"\n+class CRSError(ValueError):\n+    \"\"\"Raised when a CRS string or mapping is invalid or cannot serve\n+    to define a coordinate transformation.\"\"\"\n \n \n class DriverRegistrationError(ValueError):\n-    \"\"\"To be raised when, eg, _gdal.GDALGetDriverByName(\"MEM\") returns NULL.\"\"\"\n+    \"\"\"Raised when a format driver is requested but is not registered.\"\"\"\n \n \n class FileOverwriteError(FileError):\n-    \"\"\"Rasterio's CLI refuses to implicitly clobber output files.\"\"\"\n+    \"\"\"Raised when Rasterio's CLI refuses to clobber output files.\"\"\"\n \n     def __init__(self, message):\n         super(FileOverwriteError, self).__init__('', hint=message)\n+\n+\n+class RasterioIOError(IOError):\n+    \"\"\"Raised when a dataset cannot be opened using one of the\n+    registered format drivers.\"\"\"\ndiff --git a/rasterio/rio/warp.py b/rasterio/rio/warp.py\n--- a/rasterio/rio/warp.py\n+++ b/rasterio/rio/warp.py\n@@ -9,6 +9,7 @@\n from . import options\n import rasterio\n from rasterio import crs\n+from rasterio.errors import CRSError\n from rasterio.transform import Affine\n from rasterio.warp import (reproject, Resampling, calculate_default_transform,\n    transform_bounds)\n@@ -159,16 +160,20 @@\n             elif dst_crs:\n                 try:\n                     dst_crs = crs.from_string(dst_crs)\n-                except ValueError:\n-                    raise click.BadParameter(\"invalid crs format.\",\n-                                             param=dst_crs, param_hint=dst_crs)\n+                except ValueError as err:\n+                    raise click.BadParameter(\n+                        str(err), param='dst_crs', param_hint='dst_crs')\n \n                 if dimensions:\n                     # Calculate resolution appropriate for dimensions\n                     # in target.\n                     dst_width, dst_height = dimensions\n-                    xmin, ymin, xmax, ymax = transform_bounds(src.crs, dst_crs,\n-                                                              *src.bounds)\n+                    try:\n+                        xmin, ymin, xmax, ymax = transform_bounds(\n+                            src.crs, dst_crs, *src.bounds)\n+                    except CRSError as err:\n+                        raise click.BadParameter(\n+                            str(err), param='dst_crs', param_hint='dst_crs')\n                     dst_transform = Affine(\n                         (xmax - xmin) / float(dst_width),\n                         0, xmin, 0,\n@@ -183,8 +188,13 @@\n                             param='res', param_hint='res')\n \n                     if src_bounds:\n-                        xmin, ymin, xmax, ymax = transform_bounds(\n-                            src.crs, dst_crs, *src_bounds)\n+                        try:\n+                            xmin, ymin, xmax, ymax = transform_bounds(\n+                                src.crs, dst_crs, *src_bounds)\n+                        except CRSError as err:\n+                            raise click.BadParameter(\n+                                str(err), param='dst_crs',\n+                                param_hint='dst_crs')\n                     else:\n                         xmin, ymin, xmax, ymax = dst_bounds\n \n@@ -193,10 +203,13 @@\n                     dst_height = max(int(ceil((ymax - ymin) / res[1])), 1)\n \n                 else:\n-                    dst_transform, dst_width, dst_height = calculate_default_transform(\n-                        src.crs, dst_crs, src.width, src.height, *src.bounds,\n-                        resolution=res)\n-\n+                    try:\n+                        dst_transform, dst_width, dst_height = calculate_default_transform(\n+                            src.crs, dst_crs, src.width, src.height,\n+                            *src.bounds, resolution=res)\n+                    except CRSError as err:\n+                        raise click.BadParameter(\n+                            str(err), param='dst_crs', param_hint='dst_crs')\n             elif dimensions:\n                 # Same projection, different dimensions, calculate resolution.\n                 dst_crs = src.crs\n", "issue": "rio warp null transformer error with bad proj4\nCurrently, if you pass a bad projection, you get the following behavior:\n\n```\n$ rio warp --dst-crs \"+proj=foobar\" tests/data/warp_test.tif /tmp/foo.tif\nERROR:GDAL:CPLE_NotSupported in Failed to initialize PROJ.4 with `+proj=foobar +wktext'.\nTraceback (most recent call last):\n...\n  File \"/Users/mperry/work/rasterio/rasterio/rio/warp.py\", line 198, in warp\n    resolution=res)\n  File \"/Users/mperry/work/rasterio/rasterio/warp.py\", line 296, in calculate_default_transform\n    left, bottom, right, top)\n  File \"rasterio/_warp.pyx\", line 535, in rasterio._warp._calculate_default_transform (rasterio/_warp.cpp:9551)\n    with InMemoryRaster(\n  File \"rasterio/_warp.pyx\", line 542, in rasterio._warp._calculate_default_transform (rasterio/_warp.cpp:9261)\n    raise ValueError(\"NULL transformer\")\nValueError: NULL transformer\n```\n\nThe transformer fails to initialize, which is reasonable considering the invalid proj string. Is there any way to catch that error and report back something more meaningful than \"NULL transformer\"?\n\n", "before_files": [{"content": "import logging\nfrom math import ceil\nimport warnings\n\nimport click\nfrom cligj import files_inout_arg, format_opt\n\nfrom .helpers import resolve_inout\nfrom . import options\nimport rasterio\nfrom rasterio import crs\nfrom rasterio.transform import Affine\nfrom rasterio.warp import (reproject, Resampling, calculate_default_transform,\n   transform_bounds)\n\n\n# Improper usage of rio-warp can lead to accidental creation of\n# extremely large datasets. We'll put a hard limit on the size of\n# datasets and raise a usage error if the limits are exceeded.\nMAX_OUTPUT_WIDTH = 100000\nMAX_OUTPUT_HEIGHT = 100000\n\n\ndef bounds_handler(ctx, param, value):\n    \"\"\"Warn about future usage changes.\"\"\"\n    if value:\n        click.echo(\"Future Warning: \"\n            \"the semantics of the `--bounds` option will change in Rasterio \"\n            \"version 1.0 from bounds of the source dataset to bounds of the \"\n            \"destination dataset.\", err=True)\n    return value\n\n\ndef x_dst_bounds_handler(ctx, param, value):\n    \"\"\"Warn about future usage changes.\"\"\"\n    if value:\n        click.echo(\"Future Warning: \"\n            \"the `--x-dst-bounds` option will be removed in Rasterio version \"\n            \"1.0 in favor of `--bounds`.\", err=True)\n    return value\n\n\n@click.command(short_help='Warp a raster dataset.')\n@files_inout_arg\n@options.output_opt\n@format_opt\n@options.like_file_opt\n@click.option('--dst-crs', default=None,\n              help='Target coordinate reference system.')\n@options.dimensions_opt\n@click.option(\n    '--src-bounds',\n    nargs=4, type=float, default=None,\n    help=\"Determine output extent from source bounds: left bottom right top \"\n         \"(note: for future backwards compatibility in 1.0).\")\n@click.option(\n    '--x-dst-bounds',\n    nargs=4, type=float, default=None, callback=x_dst_bounds_handler,\n    help=\"Set output extent from bounding values: left bottom right top \"\n         \"(note: this option will be removed in 1.0).\")\n@click.option(\n    '--bounds',\n    nargs=4, type=float, default=None, callback=bounds_handler,\n    help=\"Determine output extent from source bounds: left bottom right top \"\n         \"(note: the semantics of this option will change to those of \"\n         \"`--x-dst-bounds` in version 1.0).\")\n@options.resolution_opt\n@click.option('--resampling', type=click.Choice([r.name for r in Resampling]),\n              default='nearest', help=\"Resampling method.\",\n              show_default=True)\n@click.option('--threads', type=int, default=1,\n              help='Number of processing threads.')\n@click.option('--check-invert-proj', type=bool, default=True,\n              help='Constrain output to valid coordinate region in dst-crs')\n@options.force_overwrite_opt\n@options.creation_options\n@click.pass_context\ndef warp(ctx, files, output, driver, like, dst_crs, dimensions, src_bounds,\n         x_dst_bounds, bounds, res, resampling, threads, check_invert_proj,\n         force_overwrite, creation_options):\n    \"\"\"\n    Warp a raster dataset.\n\n    If a template raster is provided using the --like option, the\n    coordinate reference system, affine transform, and dimensions of\n    that raster will be used for the output.  In this case --dst-crs,\n    --bounds, --res, and --dimensions options are ignored.\n\n    \\b\n        $ rio warp input.tif output.tif --like template.tif\n\n    The output coordinate reference system may be either a PROJ.4 or\n    EPSG:nnnn string,\n\n    \\b\n        --dst-crs EPSG:4326\n        --dst-crs '+proj=longlat +ellps=WGS84 +datum=WGS84'\n\n    or a JSON text-encoded PROJ.4 object.\n\n    \\b\n        --dst-crs '{\"proj\": \"utm\", \"zone\": 18, ...}'\n\n    If --dimensions are provided, --res and --bounds are ignored.\n    Resolution is calculated based on the relationship between the\n    raster bounds in the target coordinate system and the dimensions,\n    and may produce rectangular rather than square pixels.\n\n    \\b\n        $ rio warp input.tif output.tif --dimensions 100 200 \\\\\n        > --dst-crs EPSG:4326\n\n    If --bounds are provided, --res is required if --dst-crs is provided\n    (defaults to source raster resolution otherwise).\n\n    \\b\n        $ rio warp input.tif output.tif \\\\\n        > --bounds -78 22 -76 24 --res 0.1 --dst-crs EPSG:4326\n\n    \"\"\"\n\n    verbosity = (ctx.obj and ctx.obj.get('verbosity')) or 1\n    logger = logging.getLogger('rio')\n\n    output, files = resolve_inout(\n        files=files, output=output, force_overwrite=force_overwrite)\n\n    resampling = Resampling[resampling]  # get integer code for method\n\n    if not len(res):\n        # Click sets this as an empty tuple if not provided\n        res = None\n    else:\n        # Expand one value to two if needed\n        res = (res[0], res[0]) if len(res) == 1 else res\n\n    with rasterio.drivers(CPL_DEBUG=verbosity > 2,\n                          CHECK_WITH_INVERT_PROJ=check_invert_proj):\n        with rasterio.open(files[0]) as src:\n            l, b, r, t = src.bounds\n            out_kwargs = src.meta.copy()\n            out_kwargs['driver'] = driver\n\n            # Sort out the bounds options.\n            src_bounds = bounds or src_bounds\n            dst_bounds = x_dst_bounds\n            if src_bounds and dst_bounds:\n                raise click.BadParameter(\n                    \"Source and destination bounds may not be specified \"\n                    \"simultaneously.\")\n\n            if like:\n                with rasterio.open(like) as template_ds:\n                    dst_crs = template_ds.crs\n                    dst_transform = template_ds.affine\n                    dst_height = template_ds.height\n                    dst_width = template_ds.width\n\n            elif dst_crs:\n                try:\n                    dst_crs = crs.from_string(dst_crs)\n                except ValueError:\n                    raise click.BadParameter(\"invalid crs format.\",\n                                             param=dst_crs, param_hint=dst_crs)\n\n                if dimensions:\n                    # Calculate resolution appropriate for dimensions\n                    # in target.\n                    dst_width, dst_height = dimensions\n                    xmin, ymin, xmax, ymax = transform_bounds(src.crs, dst_crs,\n                                                              *src.bounds)\n                    dst_transform = Affine(\n                        (xmax - xmin) / float(dst_width),\n                        0, xmin, 0,\n                        (ymin - ymax) / float(dst_height),\n                        ymax\n                    )\n\n                elif src_bounds or dst_bounds:\n                    if not res:\n                        raise click.BadParameter(\n                            \"Required when using --bounds.\",\n                            param='res', param_hint='res')\n\n                    if src_bounds:\n                        xmin, ymin, xmax, ymax = transform_bounds(\n                            src.crs, dst_crs, *src_bounds)\n                    else:\n                        xmin, ymin, xmax, ymax = dst_bounds\n\n                    dst_transform = Affine(res[0], 0, xmin, 0, -res[1], ymax)\n                    dst_width = max(int(ceil((xmax - xmin) / res[0])), 1)\n                    dst_height = max(int(ceil((ymax - ymin) / res[1])), 1)\n\n                else:\n                    dst_transform, dst_width, dst_height = calculate_default_transform(\n                        src.crs, dst_crs, src.width, src.height, *src.bounds,\n                        resolution=res)\n\n            elif dimensions:\n                # Same projection, different dimensions, calculate resolution.\n                dst_crs = src.crs\n                dst_width, dst_height = dimensions\n                dst_transform = Affine(\n                    (r - l) / float(dst_width),\n                    0, l, 0,\n                    (b - t) / float(dst_height),\n                    t\n                )\n\n            elif src_bounds or dst_bounds:\n                # Same projection, different dimensions and possibly\n                # different resolution.\n                if not res:\n                    res = (src.affine.a, -src.affine.e)\n\n                dst_crs = src.crs\n                xmin, ymin, xmax, ymax = (src_bounds or dst_bounds)\n                dst_transform = Affine(res[0], 0, xmin, 0, -res[1], ymax)\n                dst_width = max(int(ceil((xmax - xmin) / res[0])), 1)\n                dst_height = max(int(ceil((ymax - ymin) / res[1])), 1)\n\n            elif res:\n                # Same projection, different resolution.\n                dst_crs = src.crs\n                dst_transform = Affine(res[0], 0, l, 0, -res[1], t)\n                dst_width = max(int(ceil((r - l) / res[0])), 1)\n                dst_height = max(int(ceil((t - b) / res[1])), 1)\n\n            else:\n                dst_crs = src.crs\n                dst_transform = src.affine\n                dst_width = src.width\n                dst_height = src.height\n\n            # When the bounds option is misused, extreme values of\n            # destination width and height may result.\n            if (dst_width < 0 or dst_height < 0 or\n                    dst_width > MAX_OUTPUT_WIDTH or\n                    dst_height > MAX_OUTPUT_HEIGHT):\n                raise click.BadParameter(\n                    \"Invalid output dimensions: {0}.\".format(\n                        (dst_width, dst_height)))\n\n            out_kwargs.update({\n                'crs': dst_crs,\n                'transform': dst_transform,\n                'affine': dst_transform,\n                'width': dst_width,\n                'height': dst_height\n            })\n\n            out_kwargs.update(**creation_options)\n\n            with rasterio.open(output, 'w', **out_kwargs) as dst:\n                for i in range(1, src.count + 1):\n\n                    reproject(\n                        source=rasterio.band(src, i),\n                        destination=rasterio.band(dst, i),\n                        src_transform=src.affine,\n                        src_crs=src.crs,\n                        # src_nodata=#TODO\n                        dst_transform=out_kwargs['transform'],\n                        dst_crs=out_kwargs['crs'],\n                        # dst_nodata=#TODO\n                        resampling=resampling,\n                        num_threads=threads)\n", "path": "rasterio/rio/warp.py"}, {"content": "\"\"\"A module of errors.\"\"\"\n\nfrom click import FileError\n\n\nclass RasterioIOError(IOError):\n    \"\"\"A failure to open a dataset using the presently registered drivers.\"\"\"\n\n\nclass DriverRegistrationError(ValueError):\n    \"\"\"To be raised when, eg, _gdal.GDALGetDriverByName(\"MEM\") returns NULL.\"\"\"\n\n\nclass FileOverwriteError(FileError):\n    \"\"\"Rasterio's CLI refuses to implicitly clobber output files.\"\"\"\n\n    def __init__(self, message):\n        super(FileOverwriteError, self).__init__('', hint=message)\n", "path": "rasterio/errors.py"}], "after_files": [{"content": "import logging\nfrom math import ceil\nimport warnings\n\nimport click\nfrom cligj import files_inout_arg, format_opt\n\nfrom .helpers import resolve_inout\nfrom . import options\nimport rasterio\nfrom rasterio import crs\nfrom rasterio.errors import CRSError\nfrom rasterio.transform import Affine\nfrom rasterio.warp import (reproject, Resampling, calculate_default_transform,\n   transform_bounds)\n\n\n# Improper usage of rio-warp can lead to accidental creation of\n# extremely large datasets. We'll put a hard limit on the size of\n# datasets and raise a usage error if the limits are exceeded.\nMAX_OUTPUT_WIDTH = 100000\nMAX_OUTPUT_HEIGHT = 100000\n\n\ndef bounds_handler(ctx, param, value):\n    \"\"\"Warn about future usage changes.\"\"\"\n    if value:\n        click.echo(\"Future Warning: \"\n            \"the semantics of the `--bounds` option will change in Rasterio \"\n            \"version 1.0 from bounds of the source dataset to bounds of the \"\n            \"destination dataset.\", err=True)\n    return value\n\n\ndef x_dst_bounds_handler(ctx, param, value):\n    \"\"\"Warn about future usage changes.\"\"\"\n    if value:\n        click.echo(\"Future Warning: \"\n            \"the `--x-dst-bounds` option will be removed in Rasterio version \"\n            \"1.0 in favor of `--bounds`.\", err=True)\n    return value\n\n\n@click.command(short_help='Warp a raster dataset.')\n@files_inout_arg\n@options.output_opt\n@format_opt\n@options.like_file_opt\n@click.option('--dst-crs', default=None,\n              help='Target coordinate reference system.')\n@options.dimensions_opt\n@click.option(\n    '--src-bounds',\n    nargs=4, type=float, default=None,\n    help=\"Determine output extent from source bounds: left bottom right top \"\n         \"(note: for future backwards compatibility in 1.0).\")\n@click.option(\n    '--x-dst-bounds',\n    nargs=4, type=float, default=None, callback=x_dst_bounds_handler,\n    help=\"Set output extent from bounding values: left bottom right top \"\n         \"(note: this option will be removed in 1.0).\")\n@click.option(\n    '--bounds',\n    nargs=4, type=float, default=None, callback=bounds_handler,\n    help=\"Determine output extent from source bounds: left bottom right top \"\n         \"(note: the semantics of this option will change to those of \"\n         \"`--x-dst-bounds` in version 1.0).\")\n@options.resolution_opt\n@click.option('--resampling', type=click.Choice([r.name for r in Resampling]),\n              default='nearest', help=\"Resampling method.\",\n              show_default=True)\n@click.option('--threads', type=int, default=1,\n              help='Number of processing threads.')\n@click.option('--check-invert-proj', type=bool, default=True,\n              help='Constrain output to valid coordinate region in dst-crs')\n@options.force_overwrite_opt\n@options.creation_options\n@click.pass_context\ndef warp(ctx, files, output, driver, like, dst_crs, dimensions, src_bounds,\n         x_dst_bounds, bounds, res, resampling, threads, check_invert_proj,\n         force_overwrite, creation_options):\n    \"\"\"\n    Warp a raster dataset.\n\n    If a template raster is provided using the --like option, the\n    coordinate reference system, affine transform, and dimensions of\n    that raster will be used for the output.  In this case --dst-crs,\n    --bounds, --res, and --dimensions options are ignored.\n\n    \\b\n        $ rio warp input.tif output.tif --like template.tif\n\n    The output coordinate reference system may be either a PROJ.4 or\n    EPSG:nnnn string,\n\n    \\b\n        --dst-crs EPSG:4326\n        --dst-crs '+proj=longlat +ellps=WGS84 +datum=WGS84'\n\n    or a JSON text-encoded PROJ.4 object.\n\n    \\b\n        --dst-crs '{\"proj\": \"utm\", \"zone\": 18, ...}'\n\n    If --dimensions are provided, --res and --bounds are ignored.\n    Resolution is calculated based on the relationship between the\n    raster bounds in the target coordinate system and the dimensions,\n    and may produce rectangular rather than square pixels.\n\n    \\b\n        $ rio warp input.tif output.tif --dimensions 100 200 \\\\\n        > --dst-crs EPSG:4326\n\n    If --bounds are provided, --res is required if --dst-crs is provided\n    (defaults to source raster resolution otherwise).\n\n    \\b\n        $ rio warp input.tif output.tif \\\\\n        > --bounds -78 22 -76 24 --res 0.1 --dst-crs EPSG:4326\n\n    \"\"\"\n\n    verbosity = (ctx.obj and ctx.obj.get('verbosity')) or 1\n    logger = logging.getLogger('rio')\n\n    output, files = resolve_inout(\n        files=files, output=output, force_overwrite=force_overwrite)\n\n    resampling = Resampling[resampling]  # get integer code for method\n\n    if not len(res):\n        # Click sets this as an empty tuple if not provided\n        res = None\n    else:\n        # Expand one value to two if needed\n        res = (res[0], res[0]) if len(res) == 1 else res\n\n    with rasterio.drivers(CPL_DEBUG=verbosity > 2,\n                          CHECK_WITH_INVERT_PROJ=check_invert_proj):\n        with rasterio.open(files[0]) as src:\n            l, b, r, t = src.bounds\n            out_kwargs = src.meta.copy()\n            out_kwargs['driver'] = driver\n\n            # Sort out the bounds options.\n            src_bounds = bounds or src_bounds\n            dst_bounds = x_dst_bounds\n            if src_bounds and dst_bounds:\n                raise click.BadParameter(\n                    \"Source and destination bounds may not be specified \"\n                    \"simultaneously.\")\n\n            if like:\n                with rasterio.open(like) as template_ds:\n                    dst_crs = template_ds.crs\n                    dst_transform = template_ds.affine\n                    dst_height = template_ds.height\n                    dst_width = template_ds.width\n\n            elif dst_crs:\n                try:\n                    dst_crs = crs.from_string(dst_crs)\n                except ValueError as err:\n                    raise click.BadParameter(\n                        str(err), param='dst_crs', param_hint='dst_crs')\n\n                if dimensions:\n                    # Calculate resolution appropriate for dimensions\n                    # in target.\n                    dst_width, dst_height = dimensions\n                    try:\n                        xmin, ymin, xmax, ymax = transform_bounds(\n                            src.crs, dst_crs, *src.bounds)\n                    except CRSError as err:\n                        raise click.BadParameter(\n                            str(err), param='dst_crs', param_hint='dst_crs')\n                    dst_transform = Affine(\n                        (xmax - xmin) / float(dst_width),\n                        0, xmin, 0,\n                        (ymin - ymax) / float(dst_height),\n                        ymax\n                    )\n\n                elif src_bounds or dst_bounds:\n                    if not res:\n                        raise click.BadParameter(\n                            \"Required when using --bounds.\",\n                            param='res', param_hint='res')\n\n                    if src_bounds:\n                        try:\n                            xmin, ymin, xmax, ymax = transform_bounds(\n                                src.crs, dst_crs, *src_bounds)\n                        except CRSError as err:\n                            raise click.BadParameter(\n                                str(err), param='dst_crs',\n                                param_hint='dst_crs')\n                    else:\n                        xmin, ymin, xmax, ymax = dst_bounds\n\n                    dst_transform = Affine(res[0], 0, xmin, 0, -res[1], ymax)\n                    dst_width = max(int(ceil((xmax - xmin) / res[0])), 1)\n                    dst_height = max(int(ceil((ymax - ymin) / res[1])), 1)\n\n                else:\n                    try:\n                        dst_transform, dst_width, dst_height = calculate_default_transform(\n                            src.crs, dst_crs, src.width, src.height,\n                            *src.bounds, resolution=res)\n                    except CRSError as err:\n                        raise click.BadParameter(\n                            str(err), param='dst_crs', param_hint='dst_crs')\n            elif dimensions:\n                # Same projection, different dimensions, calculate resolution.\n                dst_crs = src.crs\n                dst_width, dst_height = dimensions\n                dst_transform = Affine(\n                    (r - l) / float(dst_width),\n                    0, l, 0,\n                    (b - t) / float(dst_height),\n                    t\n                )\n\n            elif src_bounds or dst_bounds:\n                # Same projection, different dimensions and possibly\n                # different resolution.\n                if not res:\n                    res = (src.affine.a, -src.affine.e)\n\n                dst_crs = src.crs\n                xmin, ymin, xmax, ymax = (src_bounds or dst_bounds)\n                dst_transform = Affine(res[0], 0, xmin, 0, -res[1], ymax)\n                dst_width = max(int(ceil((xmax - xmin) / res[0])), 1)\n                dst_height = max(int(ceil((ymax - ymin) / res[1])), 1)\n\n            elif res:\n                # Same projection, different resolution.\n                dst_crs = src.crs\n                dst_transform = Affine(res[0], 0, l, 0, -res[1], t)\n                dst_width = max(int(ceil((r - l) / res[0])), 1)\n                dst_height = max(int(ceil((t - b) / res[1])), 1)\n\n            else:\n                dst_crs = src.crs\n                dst_transform = src.affine\n                dst_width = src.width\n                dst_height = src.height\n\n            # When the bounds option is misused, extreme values of\n            # destination width and height may result.\n            if (dst_width < 0 or dst_height < 0 or\n                    dst_width > MAX_OUTPUT_WIDTH or\n                    dst_height > MAX_OUTPUT_HEIGHT):\n                raise click.BadParameter(\n                    \"Invalid output dimensions: {0}.\".format(\n                        (dst_width, dst_height)))\n\n            out_kwargs.update({\n                'crs': dst_crs,\n                'transform': dst_transform,\n                'affine': dst_transform,\n                'width': dst_width,\n                'height': dst_height\n            })\n\n            out_kwargs.update(**creation_options)\n\n            with rasterio.open(output, 'w', **out_kwargs) as dst:\n                for i in range(1, src.count + 1):\n\n                    reproject(\n                        source=rasterio.band(src, i),\n                        destination=rasterio.band(dst, i),\n                        src_transform=src.affine,\n                        src_crs=src.crs,\n                        # src_nodata=#TODO\n                        dst_transform=out_kwargs['transform'],\n                        dst_crs=out_kwargs['crs'],\n                        # dst_nodata=#TODO\n                        resampling=resampling,\n                        num_threads=threads)\n", "path": "rasterio/rio/warp.py"}, {"content": "\"\"\"A module of errors.\"\"\"\n\nfrom click import FileError\n\n\nclass CRSError(ValueError):\n    \"\"\"Raised when a CRS string or mapping is invalid or cannot serve\n    to define a coordinate transformation.\"\"\"\n\n\nclass DriverRegistrationError(ValueError):\n    \"\"\"Raised when a format driver is requested but is not registered.\"\"\"\n\n\nclass FileOverwriteError(FileError):\n    \"\"\"Raised when Rasterio's CLI refuses to clobber output files.\"\"\"\n\n    def __init__(self, message):\n        super(FileOverwriteError, self).__init__('', hint=message)\n\n\nclass RasterioIOError(IOError):\n    \"\"\"Raised when a dataset cannot be opened using one of the\n    registered format drivers.\"\"\"\n", "path": "rasterio/errors.py"}]}