We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Return None when a dataset has an undefined CRS
Instead of `CRS()`. Returning `CRS()` means that application-level concerns about what an undefined CRS means become Rasterio's concerns. `None` is what we have for undefined things in Python, and using it will help us tackle the concerns at the proper level.

--- 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/edit_info.py`
Content:
```
1 """Fetch and edit raster dataset metadata from the command line."""
2 
3 
4 import json
5 import warnings
6 
7 import click
8 
9 import rasterio
10 import rasterio.crs
11 from rasterio.crs import CRS
12 from rasterio.errors import CRSError
13 from rasterio.rio import options
14 from rasterio.transform import guard_transform
15 
16 
17 # Handlers for info module options.
18 
19 def all_handler(ctx, param, value):
20     """Get tags from a template file or command line."""
21     if ctx.obj and ctx.obj.get('like') and value is not None:
22         ctx.obj['all_like'] = value
23         value = ctx.obj.get('like')
24     return value
25 
26 
27 def crs_handler(ctx, param, value):
28     """Get crs value from a template file or command line."""
29     retval = options.from_like_context(ctx, param, value)
30     if retval is None and value:
31         try:
32             retval = json.loads(value)
33         except ValueError:
34             retval = value
35         try:
36             if isinstance(retval, dict):
37                 retval = CRS(retval)
38             else:
39                 retval = CRS.from_string(retval)
40         except CRSError:
41             raise click.BadParameter(
42                 "'%s' is not a recognized CRS." % retval,
43                 param=param, param_hint='crs')
44     return retval
45 
46 
47 def tags_handler(ctx, param, value):
48     """Get tags from a template file or command line."""
49     retval = options.from_like_context(ctx, param, value)
50     if retval is None and value:
51         try:
52             retval = dict(p.split('=') for p in value)
53         except:
54             raise click.BadParameter(
55                 "'%s' contains a malformed tag." % value,
56                 param=param, param_hint='transform')
57     return retval
58 
59 
60 def transform_handler(ctx, param, value):
61     """Get transform value from a template file or command line."""
62     retval = options.from_like_context(ctx, param, value)
63     if retval is None and value:
64         try:
65             value = json.loads(value)
66         except ValueError:
67             pass
68         try:
69             retval = guard_transform(value)
70         except:
71             raise click.BadParameter(
72                 "'%s' is not recognized as an Affine array." % value,
73                 param=param, param_hint='transform')
74     return retval
75 
76 
77 @click.command('edit-info', short_help="Edit dataset metadata.")
78 @options.file_in_arg
79 @options.bidx_opt
80 @options.edit_nodata_opt
81 @click.option('--unset-nodata', default=False, is_flag=True,
82               help="Unset the dataset's nodata value.")
83 @click.option('--crs', callback=crs_handler, default=None,
84               help="New coordinate reference system")
85 @click.option('--unset-crs', default=False, is_flag=True,
86               help="Unset the dataset's CRS value.")
87 @click.option('--transform', callback=transform_handler,
88               help="New affine transform matrix")
89 @click.option('--units', help="Edit units of a band (requires --bidx)")
90 @click.option('--description',
91               help="Edit description of a band (requires --bidx)")
92 @click.option('--tag', 'tags', callback=tags_handler, multiple=True,
93               metavar='KEY=VAL', help="New tag.")
94 @click.option('--all', 'allmd', callback=all_handler, flag_value='like',
95               is_eager=True, default=False,
96               help="Copy all metadata items from the template file.")
97 @options.like_opt
98 @click.pass_context
99 def edit(ctx, input, bidx, nodata, unset_nodata, crs, unset_crs, transform,
100          units, description, tags, allmd, like):
101     """Edit a dataset's metadata: coordinate reference system, affine
102     transformation matrix, nodata value, and tags.
103 
104     The coordinate reference system may be either a PROJ.4 or EPSG:nnnn
105     string,
106 
107       --crs 'EPSG:4326'
108 
109     or a JSON text-encoded PROJ.4 object.
110 
111       --crs '{"proj": "utm", "zone": 18, ...}'
112 
113     Transforms are JSON-encoded Affine objects like:
114 
115       --transform '[300.038, 0.0, 101985.0, 0.0, -300.042, 2826915.0]'
116 
117     Prior to Rasterio 1.0 GDAL geotransforms were supported for --transform,
118     but are no longer supported.
119 
120     Metadata items may also be read from an existing dataset using a
121     combination of the --like option with at least one of --all,
122     `--crs like`, `--nodata like`, and `--transform like`.
123 
124       rio edit-info example.tif --like template.tif --all
125 
126     To get just the transform from the template:
127 
128       rio edit-info example.tif --like template.tif --transform like
129 
130     """
131     import numpy as np
132 
133     def in_dtype_range(value, dtype):
134         infos = {'c': np.finfo, 'f': np.finfo, 'i': np.iinfo,
135                  'u': np.iinfo}
136         rng = infos[np.dtype(dtype).kind](dtype)
137         return rng.min <= value <= rng.max
138 
139     with ctx.obj['env'], rasterio.open(input, 'r+') as dst:
140 
141         if allmd:
142             nodata = allmd['nodata']
143             crs = allmd['crs']
144             transform = allmd['transform']
145             tags = allmd['tags']
146 
147         if unset_nodata and nodata is not options.IgnoreOption:
148             raise click.BadParameter(
149                 "--unset-nodata and --nodata cannot be used together.")
150 
151         if unset_crs and crs:
152             raise click.BadParameter(
153                 "--unset-crs and --crs cannot be used together.")
154 
155         if unset_nodata:
156             # Setting nodata to None will raise NotImplementedError
157             # if GDALDeleteRasterNoDataValue() isn't present in the
158             # GDAL library.
159             try:
160                 dst.nodata = None
161             except NotImplementedError as exc:  # pragma: no cover
162                 raise click.ClickException(str(exc))
163 
164         elif nodata is not options.IgnoreOption:
165             dtype = dst.dtypes[0]
166             if nodata is not None and not in_dtype_range(nodata, dtype):
167                 raise click.BadParameter(
168                     "outside the range of the file's "
169                     "data type (%s)." % dtype,
170                     param=nodata, param_hint='nodata')
171             dst.nodata = nodata
172 
173         if unset_crs:
174             dst.crs = CRS()
175         elif crs:
176             dst.crs = crs
177 
178         if transform:
179             dst.transform = transform
180 
181         if tags:
182             dst.update_tags(**tags)
183 
184         if units:
185             dst.set_units(bidx, units)
186 
187         if description:
188             dst.set_description(bidx, description)
189 
190     # Post check - ensure that crs was unset properly
191     if unset_crs:
192         with ctx.obj['env'], rasterio.open(input, 'r') as src:
193             if dict(src.crs) != {}:
194                 warnings.warn(
195                     'CRS was not unset. Availability of his functionality '
196                     'differs depending on GDAL version and driver')
197
```

Path: `rasterio/rio/info.py`
Content:
```
1 """Command access to dataset metadata, stats, and more."""
2 
3 
4 import json
5 
6 import click
7 
8 import rasterio
9 import rasterio.crs
10 from rasterio.rio import options
11 
12 
13 @click.command(short_help="Print information about a data file.")
14 @options.file_in_arg
15 @click.option('--meta', 'aspect', flag_value='meta', default=True,
16               help="Show data file structure (default).")
17 @click.option('--tags', 'aspect', flag_value='tags',
18               help="Show data file tags.")
19 @click.option('--namespace', help="Select a tag namespace.")
20 @click.option('--indent', default=None, type=int,
21               help="Indentation level for pretty printed output")
22 # Options to pick out a single metadata item and print it as
23 # a string.
24 @click.option('--count', 'meta_member', flag_value='count',
25               help="Print the count of bands.")
26 @click.option('-t', '--dtype', 'meta_member', flag_value='dtype',
27               help="Print the dtype name.")
28 @click.option('--nodata', 'meta_member', flag_value='nodata',
29               help="Print the nodata value.")
30 @click.option('-f', '--format', '--driver', 'meta_member', flag_value='driver',
31               help="Print the format driver.")
32 @click.option('--shape', 'meta_member', flag_value='shape',
33               help="Print the (height, width) shape.")
34 @click.option('--height', 'meta_member', flag_value='height',
35               help="Print the height (number of rows).")
36 @click.option('--width', 'meta_member', flag_value='width',
37               help="Print the width (number of columns).")
38 @click.option('--crs', 'meta_member', flag_value='crs',
39               help="Print the CRS as a PROJ.4 string.")
40 @click.option('--bounds', 'meta_member', flag_value='bounds',
41               help="Print the boundary coordinates "
42                    "(left, bottom, right, top).")
43 @click.option('-r', '--res', 'meta_member', flag_value='res',
44               help="Print pixel width and height.")
45 @click.option('--lnglat', 'meta_member', flag_value='lnglat',
46               help="Print longitude and latitude at center.")
47 @click.option('--stats', 'meta_member', flag_value='stats',
48               help="Print statistics (min, max, mean) of a single band "
49                    "(use --bidx).")
50 @click.option('--checksum', 'meta_member', flag_value='checksum',
51               help="Print integer checksum of a single band "
52                    "(use --bidx).")
53 @click.option('-v', '--tell-me-more', '--verbose', is_flag=True,
54               help="Output extra information.")
55 @options.bidx_opt
56 @options.masked_opt
57 @click.pass_context
58 def info(ctx, input, aspect, indent, namespace, meta_member, verbose, bidx,
59          masked):
60     """Print metadata about the dataset as JSON.
61 
62     Optionally print a single metadata item as a string.
63     """
64     try:
65         with ctx.obj['env'], rasterio.open(input) as src:
66 
67             info = dict(src.profile)
68             info['shape'] = (info['height'], info['width'])
69             info['bounds'] = src.bounds
70             proj4 = src.crs.to_string()
71             if proj4.startswith('+init=epsg'):
72                 proj4 = proj4.split('=')[1].upper()
73             info['crs'] = proj4
74             info['res'] = src.res
75             info['colorinterp'] = [src.colorinterp(i).name
76                                    for i in src.indexes]
77             info['units'] = [units or None for units in src.units]
78             info['descriptions'] = src.descriptions
79             info['indexes'] = src.indexes
80             info['mask_flags'] = [[
81                 flag.name for flag in flags] for flags in src.mask_flag_enums]
82 
83             if proj4 != '':
84                 info['lnglat'] = src.lnglat()
85 
86             if verbose:
87                 stats = [{'min': float(b.min()),
88                           'max': float(b.max()),
89                           'mean': float(b.mean())
90                           } for b in src.read(masked=masked)]
91                 info['stats'] = stats
92 
93                 info['checksum'] = [src.checksum(i) for i in src.indexes]
94 
95                 gcps, crs = src.gcps
96                 proj4 = crs.to_string()
97                 if proj4.startswith('+init=epsg'):
98                     proj4 = proj4.split('=')[1].upper()
99                 if gcps:
100                     info['gcps'] = {
101                         'crs': proj4, 'points': [p.asdict() for p in gcps]}
102 
103             if aspect == 'meta':
104                 if meta_member == 'stats':
105                     band = src.read(bidx, masked=masked)
106                     click.echo('%f %f %f' % (
107                         float(band.min()),
108                         float(band.max()),
109                         float(band.mean())))
110                 elif meta_member == 'checksum':
111                     click.echo(str(src.checksum(bidx)))
112                 elif meta_member:
113                     if isinstance(info[meta_member], (list, tuple)):
114                         click.echo(" ".join(map(str, info[meta_member])))
115                     else:
116                         click.echo(info[meta_member])
117                 else:
118                     click.echo(json.dumps(info, sort_keys=True, indent=indent))
119 
120             elif aspect == 'tags':
121                 click.echo(
122                     json.dumps(src.tags(ns=namespace), indent=indent))
123     except Exception:
124         raise click.Abort()
125
```
--- 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/rio/edit_info.py b/rasterio/rio/edit_info.py
--- a/rasterio/rio/edit_info.py
+++ b/rasterio/rio/edit_info.py
@@ -171,7 +171,7 @@
             dst.nodata = nodata
 
         if unset_crs:
-            dst.crs = CRS()
+            dst.crs = None  # CRS()
         elif crs:
             dst.crs = crs
 
@@ -190,7 +190,7 @@
     # Post check - ensure that crs was unset properly
     if unset_crs:
         with ctx.obj['env'], rasterio.open(input, 'r') as src:
-            if dict(src.crs) != {}:
+            if src.crs:
                 warnings.warn(
                     'CRS was not unset. Availability of his functionality '
                     'differs depending on GDAL version and driver')
diff --git a/rasterio/rio/info.py b/rasterio/rio/info.py
--- a/rasterio/rio/info.py
+++ b/rasterio/rio/info.py
@@ -67,10 +67,15 @@
             info = dict(src.profile)
             info['shape'] = (info['height'], info['width'])
             info['bounds'] = src.bounds
-            proj4 = src.crs.to_string()
-            if proj4.startswith('+init=epsg'):
-                proj4 = proj4.split('=')[1].upper()
-            info['crs'] = proj4
+
+            if src.crs:
+                proj4 = src.crs.to_string()
+                if proj4.startswith('+init=epsg'):
+                    proj4 = proj4.split('=')[1].upper()
+                info['crs'] = proj4
+            else:
+                info['crs'] = None
+
             info['res'] = src.res
             info['colorinterp'] = [src.colorinterp(i).name
                                    for i in src.indexes]

{"golden_diff": "diff --git a/rasterio/rio/edit_info.py b/rasterio/rio/edit_info.py\n--- a/rasterio/rio/edit_info.py\n+++ b/rasterio/rio/edit_info.py\n@@ -171,7 +171,7 @@\n             dst.nodata = nodata\n \n         if unset_crs:\n-            dst.crs = CRS()\n+            dst.crs = None  # CRS()\n         elif crs:\n             dst.crs = crs\n \n@@ -190,7 +190,7 @@\n     # Post check - ensure that crs was unset properly\n     if unset_crs:\n         with ctx.obj['env'], rasterio.open(input, 'r') as src:\n-            if dict(src.crs) != {}:\n+            if src.crs:\n                 warnings.warn(\n                     'CRS was not unset. Availability of his functionality '\n                     'differs depending on GDAL version and driver')\ndiff --git a/rasterio/rio/info.py b/rasterio/rio/info.py\n--- a/rasterio/rio/info.py\n+++ b/rasterio/rio/info.py\n@@ -67,10 +67,15 @@\n             info = dict(src.profile)\n             info['shape'] = (info['height'], info['width'])\n             info['bounds'] = src.bounds\n-            proj4 = src.crs.to_string()\n-            if proj4.startswith('+init=epsg'):\n-                proj4 = proj4.split('=')[1].upper()\n-            info['crs'] = proj4\n+\n+            if src.crs:\n+                proj4 = src.crs.to_string()\n+                if proj4.startswith('+init=epsg'):\n+                    proj4 = proj4.split('=')[1].upper()\n+                info['crs'] = proj4\n+            else:\n+                info['crs'] = None\n+\n             info['res'] = src.res\n             info['colorinterp'] = [src.colorinterp(i).name\n                                    for i in src.indexes]\n", "issue": "Return None when a dataset has an undefined CRS\nInstead of `CRS()`. Returning `CRS()` means that application-level concerns about what an undefined CRS means become Rasterio's concerns. `None` is what we have for undefined things in Python, and using it will help us tackle the concerns at the proper level.\n", "before_files": [{"content": "\"\"\"Fetch and edit raster dataset metadata from the command line.\"\"\"\n\n\nimport json\nimport warnings\n\nimport click\n\nimport rasterio\nimport rasterio.crs\nfrom rasterio.crs import CRS\nfrom rasterio.errors import CRSError\nfrom rasterio.rio import options\nfrom rasterio.transform import guard_transform\n\n\n# Handlers for info module options.\n\ndef all_handler(ctx, param, value):\n    \"\"\"Get tags from a template file or command line.\"\"\"\n    if ctx.obj and ctx.obj.get('like') and value is not None:\n        ctx.obj['all_like'] = value\n        value = ctx.obj.get('like')\n    return value\n\n\ndef crs_handler(ctx, param, value):\n    \"\"\"Get crs value from a template file or command line.\"\"\"\n    retval = options.from_like_context(ctx, param, value)\n    if retval is None and value:\n        try:\n            retval = json.loads(value)\n        except ValueError:\n            retval = value\n        try:\n            if isinstance(retval, dict):\n                retval = CRS(retval)\n            else:\n                retval = CRS.from_string(retval)\n        except CRSError:\n            raise click.BadParameter(\n                \"'%s' is not a recognized CRS.\" % retval,\n                param=param, param_hint='crs')\n    return retval\n\n\ndef tags_handler(ctx, param, value):\n    \"\"\"Get tags from a template file or command line.\"\"\"\n    retval = options.from_like_context(ctx, param, value)\n    if retval is None and value:\n        try:\n            retval = dict(p.split('=') for p in value)\n        except:\n            raise click.BadParameter(\n                \"'%s' contains a malformed tag.\" % value,\n                param=param, param_hint='transform')\n    return retval\n\n\ndef transform_handler(ctx, param, value):\n    \"\"\"Get transform value from a template file or command line.\"\"\"\n    retval = options.from_like_context(ctx, param, value)\n    if retval is None and value:\n        try:\n            value = json.loads(value)\n        except ValueError:\n            pass\n        try:\n            retval = guard_transform(value)\n        except:\n            raise click.BadParameter(\n                \"'%s' is not recognized as an Affine array.\" % value,\n                param=param, param_hint='transform')\n    return retval\n\n\n@click.command('edit-info', short_help=\"Edit dataset metadata.\")\n@options.file_in_arg\n@options.bidx_opt\n@options.edit_nodata_opt\n@click.option('--unset-nodata', default=False, is_flag=True,\n              help=\"Unset the dataset's nodata value.\")\n@click.option('--crs', callback=crs_handler, default=None,\n              help=\"New coordinate reference system\")\n@click.option('--unset-crs', default=False, is_flag=True,\n              help=\"Unset the dataset's CRS value.\")\n@click.option('--transform', callback=transform_handler,\n              help=\"New affine transform matrix\")\n@click.option('--units', help=\"Edit units of a band (requires --bidx)\")\n@click.option('--description',\n              help=\"Edit description of a band (requires --bidx)\")\n@click.option('--tag', 'tags', callback=tags_handler, multiple=True,\n              metavar='KEY=VAL', help=\"New tag.\")\n@click.option('--all', 'allmd', callback=all_handler, flag_value='like',\n              is_eager=True, default=False,\n              help=\"Copy all metadata items from the template file.\")\n@options.like_opt\n@click.pass_context\ndef edit(ctx, input, bidx, nodata, unset_nodata, crs, unset_crs, transform,\n         units, description, tags, allmd, like):\n    \"\"\"Edit a dataset's metadata: coordinate reference system, affine\n    transformation matrix, nodata value, and tags.\n\n    The coordinate reference system may be either a PROJ.4 or EPSG:nnnn\n    string,\n\n      --crs 'EPSG:4326'\n\n    or a JSON text-encoded PROJ.4 object.\n\n      --crs '{\"proj\": \"utm\", \"zone\": 18, ...}'\n\n    Transforms are JSON-encoded Affine objects like:\n\n      --transform '[300.038, 0.0, 101985.0, 0.0, -300.042, 2826915.0]'\n\n    Prior to Rasterio 1.0 GDAL geotransforms were supported for --transform,\n    but are no longer supported.\n\n    Metadata items may also be read from an existing dataset using a\n    combination of the --like option with at least one of --all,\n    `--crs like`, `--nodata like`, and `--transform like`.\n\n      rio edit-info example.tif --like template.tif --all\n\n    To get just the transform from the template:\n\n      rio edit-info example.tif --like template.tif --transform like\n\n    \"\"\"\n    import numpy as np\n\n    def in_dtype_range(value, dtype):\n        infos = {'c': np.finfo, 'f': np.finfo, 'i': np.iinfo,\n                 'u': np.iinfo}\n        rng = infos[np.dtype(dtype).kind](dtype)\n        return rng.min <= value <= rng.max\n\n    with ctx.obj['env'], rasterio.open(input, 'r+') as dst:\n\n        if allmd:\n            nodata = allmd['nodata']\n            crs = allmd['crs']\n            transform = allmd['transform']\n            tags = allmd['tags']\n\n        if unset_nodata and nodata is not options.IgnoreOption:\n            raise click.BadParameter(\n                \"--unset-nodata and --nodata cannot be used together.\")\n\n        if unset_crs and crs:\n            raise click.BadParameter(\n                \"--unset-crs and --crs cannot be used together.\")\n\n        if unset_nodata:\n            # Setting nodata to None will raise NotImplementedError\n            # if GDALDeleteRasterNoDataValue() isn't present in the\n            # GDAL library.\n            try:\n                dst.nodata = None\n            except NotImplementedError as exc:  # pragma: no cover\n                raise click.ClickException(str(exc))\n\n        elif nodata is not options.IgnoreOption:\n            dtype = dst.dtypes[0]\n            if nodata is not None and not in_dtype_range(nodata, dtype):\n                raise click.BadParameter(\n                    \"outside the range of the file's \"\n                    \"data type (%s).\" % dtype,\n                    param=nodata, param_hint='nodata')\n            dst.nodata = nodata\n\n        if unset_crs:\n            dst.crs = CRS()\n        elif crs:\n            dst.crs = crs\n\n        if transform:\n            dst.transform = transform\n\n        if tags:\n            dst.update_tags(**tags)\n\n        if units:\n            dst.set_units(bidx, units)\n\n        if description:\n            dst.set_description(bidx, description)\n\n    # Post check - ensure that crs was unset properly\n    if unset_crs:\n        with ctx.obj['env'], rasterio.open(input, 'r') as src:\n            if dict(src.crs) != {}:\n                warnings.warn(\n                    'CRS was not unset. Availability of his functionality '\n                    'differs depending on GDAL version and driver')\n", "path": "rasterio/rio/edit_info.py"}, {"content": "\"\"\"Command access to dataset metadata, stats, and more.\"\"\"\n\n\nimport json\n\nimport click\n\nimport rasterio\nimport rasterio.crs\nfrom rasterio.rio import options\n\n\n@click.command(short_help=\"Print information about a data file.\")\n@options.file_in_arg\n@click.option('--meta', 'aspect', flag_value='meta', default=True,\n              help=\"Show data file structure (default).\")\n@click.option('--tags', 'aspect', flag_value='tags',\n              help=\"Show data file tags.\")\n@click.option('--namespace', help=\"Select a tag namespace.\")\n@click.option('--indent', default=None, type=int,\n              help=\"Indentation level for pretty printed output\")\n# Options to pick out a single metadata item and print it as\n# a string.\n@click.option('--count', 'meta_member', flag_value='count',\n              help=\"Print the count of bands.\")\n@click.option('-t', '--dtype', 'meta_member', flag_value='dtype',\n              help=\"Print the dtype name.\")\n@click.option('--nodata', 'meta_member', flag_value='nodata',\n              help=\"Print the nodata value.\")\n@click.option('-f', '--format', '--driver', 'meta_member', flag_value='driver',\n              help=\"Print the format driver.\")\n@click.option('--shape', 'meta_member', flag_value='shape',\n              help=\"Print the (height, width) shape.\")\n@click.option('--height', 'meta_member', flag_value='height',\n              help=\"Print the height (number of rows).\")\n@click.option('--width', 'meta_member', flag_value='width',\n              help=\"Print the width (number of columns).\")\n@click.option('--crs', 'meta_member', flag_value='crs',\n              help=\"Print the CRS as a PROJ.4 string.\")\n@click.option('--bounds', 'meta_member', flag_value='bounds',\n              help=\"Print the boundary coordinates \"\n                   \"(left, bottom, right, top).\")\n@click.option('-r', '--res', 'meta_member', flag_value='res',\n              help=\"Print pixel width and height.\")\n@click.option('--lnglat', 'meta_member', flag_value='lnglat',\n              help=\"Print longitude and latitude at center.\")\n@click.option('--stats', 'meta_member', flag_value='stats',\n              help=\"Print statistics (min, max, mean) of a single band \"\n                   \"(use --bidx).\")\n@click.option('--checksum', 'meta_member', flag_value='checksum',\n              help=\"Print integer checksum of a single band \"\n                   \"(use --bidx).\")\n@click.option('-v', '--tell-me-more', '--verbose', is_flag=True,\n              help=\"Output extra information.\")\n@options.bidx_opt\n@options.masked_opt\n@click.pass_context\ndef info(ctx, input, aspect, indent, namespace, meta_member, verbose, bidx,\n         masked):\n    \"\"\"Print metadata about the dataset as JSON.\n\n    Optionally print a single metadata item as a string.\n    \"\"\"\n    try:\n        with ctx.obj['env'], rasterio.open(input) as src:\n\n            info = dict(src.profile)\n            info['shape'] = (info['height'], info['width'])\n            info['bounds'] = src.bounds\n            proj4 = src.crs.to_string()\n            if proj4.startswith('+init=epsg'):\n                proj4 = proj4.split('=')[1].upper()\n            info['crs'] = proj4\n            info['res'] = src.res\n            info['colorinterp'] = [src.colorinterp(i).name\n                                   for i in src.indexes]\n            info['units'] = [units or None for units in src.units]\n            info['descriptions'] = src.descriptions\n            info['indexes'] = src.indexes\n            info['mask_flags'] = [[\n                flag.name for flag in flags] for flags in src.mask_flag_enums]\n\n            if proj4 != '':\n                info['lnglat'] = src.lnglat()\n\n            if verbose:\n                stats = [{'min': float(b.min()),\n                          'max': float(b.max()),\n                          'mean': float(b.mean())\n                          } for b in src.read(masked=masked)]\n                info['stats'] = stats\n\n                info['checksum'] = [src.checksum(i) for i in src.indexes]\n\n                gcps, crs = src.gcps\n                proj4 = crs.to_string()\n                if proj4.startswith('+init=epsg'):\n                    proj4 = proj4.split('=')[1].upper()\n                if gcps:\n                    info['gcps'] = {\n                        'crs': proj4, 'points': [p.asdict() for p in gcps]}\n\n            if aspect == 'meta':\n                if meta_member == 'stats':\n                    band = src.read(bidx, masked=masked)\n                    click.echo('%f %f %f' % (\n                        float(band.min()),\n                        float(band.max()),\n                        float(band.mean())))\n                elif meta_member == 'checksum':\n                    click.echo(str(src.checksum(bidx)))\n                elif meta_member:\n                    if isinstance(info[meta_member], (list, tuple)):\n                        click.echo(\" \".join(map(str, info[meta_member])))\n                    else:\n                        click.echo(info[meta_member])\n                else:\n                    click.echo(json.dumps(info, sort_keys=True, indent=indent))\n\n            elif aspect == 'tags':\n                click.echo(\n                    json.dumps(src.tags(ns=namespace), indent=indent))\n    except Exception:\n        raise click.Abort()\n", "path": "rasterio/rio/info.py"}], "after_files": [{"content": "\"\"\"Fetch and edit raster dataset metadata from the command line.\"\"\"\n\n\nimport json\nimport warnings\n\nimport click\n\nimport rasterio\nimport rasterio.crs\nfrom rasterio.crs import CRS\nfrom rasterio.errors import CRSError\nfrom rasterio.rio import options\nfrom rasterio.transform import guard_transform\n\n\n# Handlers for info module options.\n\ndef all_handler(ctx, param, value):\n    \"\"\"Get tags from a template file or command line.\"\"\"\n    if ctx.obj and ctx.obj.get('like') and value is not None:\n        ctx.obj['all_like'] = value\n        value = ctx.obj.get('like')\n    return value\n\n\ndef crs_handler(ctx, param, value):\n    \"\"\"Get crs value from a template file or command line.\"\"\"\n    retval = options.from_like_context(ctx, param, value)\n    if retval is None and value:\n        try:\n            retval = json.loads(value)\n        except ValueError:\n            retval = value\n        try:\n            if isinstance(retval, dict):\n                retval = CRS(retval)\n            else:\n                retval = CRS.from_string(retval)\n        except CRSError:\n            raise click.BadParameter(\n                \"'%s' is not a recognized CRS.\" % retval,\n                param=param, param_hint='crs')\n    return retval\n\n\ndef tags_handler(ctx, param, value):\n    \"\"\"Get tags from a template file or command line.\"\"\"\n    retval = options.from_like_context(ctx, param, value)\n    if retval is None and value:\n        try:\n            retval = dict(p.split('=') for p in value)\n        except:\n            raise click.BadParameter(\n                \"'%s' contains a malformed tag.\" % value,\n                param=param, param_hint='transform')\n    return retval\n\n\ndef transform_handler(ctx, param, value):\n    \"\"\"Get transform value from a template file or command line.\"\"\"\n    retval = options.from_like_context(ctx, param, value)\n    if retval is None and value:\n        try:\n            value = json.loads(value)\n        except ValueError:\n            pass\n        try:\n            retval = guard_transform(value)\n        except:\n            raise click.BadParameter(\n                \"'%s' is not recognized as an Affine array.\" % value,\n                param=param, param_hint='transform')\n    return retval\n\n\n@click.command('edit-info', short_help=\"Edit dataset metadata.\")\n@options.file_in_arg\n@options.bidx_opt\n@options.edit_nodata_opt\n@click.option('--unset-nodata', default=False, is_flag=True,\n              help=\"Unset the dataset's nodata value.\")\n@click.option('--crs', callback=crs_handler, default=None,\n              help=\"New coordinate reference system\")\n@click.option('--unset-crs', default=False, is_flag=True,\n              help=\"Unset the dataset's CRS value.\")\n@click.option('--transform', callback=transform_handler,\n              help=\"New affine transform matrix\")\n@click.option('--units', help=\"Edit units of a band (requires --bidx)\")\n@click.option('--description',\n              help=\"Edit description of a band (requires --bidx)\")\n@click.option('--tag', 'tags', callback=tags_handler, multiple=True,\n              metavar='KEY=VAL', help=\"New tag.\")\n@click.option('--all', 'allmd', callback=all_handler, flag_value='like',\n              is_eager=True, default=False,\n              help=\"Copy all metadata items from the template file.\")\n@options.like_opt\n@click.pass_context\ndef edit(ctx, input, bidx, nodata, unset_nodata, crs, unset_crs, transform,\n         units, description, tags, allmd, like):\n    \"\"\"Edit a dataset's metadata: coordinate reference system, affine\n    transformation matrix, nodata value, and tags.\n\n    The coordinate reference system may be either a PROJ.4 or EPSG:nnnn\n    string,\n\n      --crs 'EPSG:4326'\n\n    or a JSON text-encoded PROJ.4 object.\n\n      --crs '{\"proj\": \"utm\", \"zone\": 18, ...}'\n\n    Transforms are JSON-encoded Affine objects like:\n\n      --transform '[300.038, 0.0, 101985.0, 0.0, -300.042, 2826915.0]'\n\n    Prior to Rasterio 1.0 GDAL geotransforms were supported for --transform,\n    but are no longer supported.\n\n    Metadata items may also be read from an existing dataset using a\n    combination of the --like option with at least one of --all,\n    `--crs like`, `--nodata like`, and `--transform like`.\n\n      rio edit-info example.tif --like template.tif --all\n\n    To get just the transform from the template:\n\n      rio edit-info example.tif --like template.tif --transform like\n\n    \"\"\"\n    import numpy as np\n\n    def in_dtype_range(value, dtype):\n        infos = {'c': np.finfo, 'f': np.finfo, 'i': np.iinfo,\n                 'u': np.iinfo}\n        rng = infos[np.dtype(dtype).kind](dtype)\n        return rng.min <= value <= rng.max\n\n    with ctx.obj['env'], rasterio.open(input, 'r+') as dst:\n\n        if allmd:\n            nodata = allmd['nodata']\n            crs = allmd['crs']\n            transform = allmd['transform']\n            tags = allmd['tags']\n\n        if unset_nodata and nodata is not options.IgnoreOption:\n            raise click.BadParameter(\n                \"--unset-nodata and --nodata cannot be used together.\")\n\n        if unset_crs and crs:\n            raise click.BadParameter(\n                \"--unset-crs and --crs cannot be used together.\")\n\n        if unset_nodata:\n            # Setting nodata to None will raise NotImplementedError\n            # if GDALDeleteRasterNoDataValue() isn't present in the\n            # GDAL library.\n            try:\n                dst.nodata = None\n            except NotImplementedError as exc:  # pragma: no cover\n                raise click.ClickException(str(exc))\n\n        elif nodata is not options.IgnoreOption:\n            dtype = dst.dtypes[0]\n            if nodata is not None and not in_dtype_range(nodata, dtype):\n                raise click.BadParameter(\n                    \"outside the range of the file's \"\n                    \"data type (%s).\" % dtype,\n                    param=nodata, param_hint='nodata')\n            dst.nodata = nodata\n\n        if unset_crs:\n            dst.crs = None  # CRS()\n        elif crs:\n            dst.crs = crs\n\n        if transform:\n            dst.transform = transform\n\n        if tags:\n            dst.update_tags(**tags)\n\n        if units:\n            dst.set_units(bidx, units)\n\n        if description:\n            dst.set_description(bidx, description)\n\n    # Post check - ensure that crs was unset properly\n    if unset_crs:\n        with ctx.obj['env'], rasterio.open(input, 'r') as src:\n            if src.crs:\n                warnings.warn(\n                    'CRS was not unset. Availability of his functionality '\n                    'differs depending on GDAL version and driver')\n", "path": "rasterio/rio/edit_info.py"}, {"content": "\"\"\"Command access to dataset metadata, stats, and more.\"\"\"\n\n\nimport json\n\nimport click\n\nimport rasterio\nimport rasterio.crs\nfrom rasterio.rio import options\n\n\n@click.command(short_help=\"Print information about a data file.\")\n@options.file_in_arg\n@click.option('--meta', 'aspect', flag_value='meta', default=True,\n              help=\"Show data file structure (default).\")\n@click.option('--tags', 'aspect', flag_value='tags',\n              help=\"Show data file tags.\")\n@click.option('--namespace', help=\"Select a tag namespace.\")\n@click.option('--indent', default=None, type=int,\n              help=\"Indentation level for pretty printed output\")\n# Options to pick out a single metadata item and print it as\n# a string.\n@click.option('--count', 'meta_member', flag_value='count',\n              help=\"Print the count of bands.\")\n@click.option('-t', '--dtype', 'meta_member', flag_value='dtype',\n              help=\"Print the dtype name.\")\n@click.option('--nodata', 'meta_member', flag_value='nodata',\n              help=\"Print the nodata value.\")\n@click.option('-f', '--format', '--driver', 'meta_member', flag_value='driver',\n              help=\"Print the format driver.\")\n@click.option('--shape', 'meta_member', flag_value='shape',\n              help=\"Print the (height, width) shape.\")\n@click.option('--height', 'meta_member', flag_value='height',\n              help=\"Print the height (number of rows).\")\n@click.option('--width', 'meta_member', flag_value='width',\n              help=\"Print the width (number of columns).\")\n@click.option('--crs', 'meta_member', flag_value='crs',\n              help=\"Print the CRS as a PROJ.4 string.\")\n@click.option('--bounds', 'meta_member', flag_value='bounds',\n              help=\"Print the boundary coordinates \"\n                   \"(left, bottom, right, top).\")\n@click.option('-r', '--res', 'meta_member', flag_value='res',\n              help=\"Print pixel width and height.\")\n@click.option('--lnglat', 'meta_member', flag_value='lnglat',\n              help=\"Print longitude and latitude at center.\")\n@click.option('--stats', 'meta_member', flag_value='stats',\n              help=\"Print statistics (min, max, mean) of a single band \"\n                   \"(use --bidx).\")\n@click.option('--checksum', 'meta_member', flag_value='checksum',\n              help=\"Print integer checksum of a single band \"\n                   \"(use --bidx).\")\n@click.option('-v', '--tell-me-more', '--verbose', is_flag=True,\n              help=\"Output extra information.\")\n@options.bidx_opt\n@options.masked_opt\n@click.pass_context\ndef info(ctx, input, aspect, indent, namespace, meta_member, verbose, bidx,\n         masked):\n    \"\"\"Print metadata about the dataset as JSON.\n\n    Optionally print a single metadata item as a string.\n    \"\"\"\n    try:\n        with ctx.obj['env'], rasterio.open(input) as src:\n\n            info = dict(src.profile)\n            info['shape'] = (info['height'], info['width'])\n            info['bounds'] = src.bounds\n\n            if src.crs:\n                proj4 = src.crs.to_string()\n                if proj4.startswith('+init=epsg'):\n                    proj4 = proj4.split('=')[1].upper()\n                info['crs'] = proj4\n            else:\n                info['crs'] = None\n\n            info['res'] = src.res\n            info['colorinterp'] = [src.colorinterp(i).name\n                                   for i in src.indexes]\n            info['units'] = [units or None for units in src.units]\n            info['descriptions'] = src.descriptions\n            info['indexes'] = src.indexes\n            info['mask_flags'] = [[\n                flag.name for flag in flags] for flags in src.mask_flag_enums]\n\n            if proj4 != '':\n                info['lnglat'] = src.lnglat()\n\n            if verbose:\n                stats = [{'min': float(b.min()),\n                          'max': float(b.max()),\n                          'mean': float(b.mean())\n                          } for b in src.read(masked=masked)]\n                info['stats'] = stats\n\n                info['checksum'] = [src.checksum(i) for i in src.indexes]\n\n                gcps, crs = src.gcps\n                proj4 = crs.to_string()\n                if proj4.startswith('+init=epsg'):\n                    proj4 = proj4.split('=')[1].upper()\n                if gcps:\n                    info['gcps'] = {\n                        'crs': proj4, 'points': [p.asdict() for p in gcps]}\n\n            if aspect == 'meta':\n                if meta_member == 'stats':\n                    band = src.read(bidx, masked=masked)\n                    click.echo('%f %f %f' % (\n                        float(band.min()),\n                        float(band.max()),\n                        float(band.mean())))\n                elif meta_member == 'checksum':\n                    click.echo(str(src.checksum(bidx)))\n                elif meta_member:\n                    if isinstance(info[meta_member], (list, tuple)):\n                        click.echo(\" \".join(map(str, info[meta_member])))\n                    else:\n                        click.echo(info[meta_member])\n                else:\n                    click.echo(json.dumps(info, sort_keys=True, indent=indent))\n\n            elif aspect == 'tags':\n                click.echo(\n                    json.dumps(src.tags(ns=namespace), indent=indent))\n    except Exception:\n        raise click.Abort()\n", "path": "rasterio/rio/info.py"}]}