mirror of
https://github.com/rasterio/rasterio.git
synced 2025-12-08 17:36:12 +00:00
Add a rio create command. (#3023)
* Add a rio create command. Resolves #3021. * Eliminate deprecated pytest.warns usage * Add CLI doc for rio create * Fix punctuation. * Add bounds option for geotransform, remove duplicate nodata option. * Warn about duplicate georeferencing
This commit is contained in:
parent
34b0d1b34d
commit
35a5906f57
@ -14,6 +14,7 @@ Deprecations:
|
||||
|
||||
New Features:
|
||||
|
||||
- The new "rio create" command allows creation of new, empty datasets (#3023).
|
||||
- An optional range keyword argument (like that of numpy.histogram()) has been
|
||||
added to show_hist() (#2873, #3001).
|
||||
- Datasets stored in proprietary systems or addressable only through protocols
|
||||
|
||||
29
docs/cli.rst
29
docs/cli.rst
@ -15,7 +15,7 @@ namespace and handling of context variables.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ rio --help ⏎
|
||||
$ rio --help
|
||||
Usage: rio [OPTIONS] COMMAND [ARGS]...
|
||||
|
||||
Rasterio command line interface.
|
||||
@ -28,6 +28,7 @@ namespace and handling of context variables.
|
||||
--aws-requester-pays Requester pays data transfer costs
|
||||
--version Show the version and exit.
|
||||
--gdal-version
|
||||
--show-versions Show dependency versions
|
||||
--help Show this message and exit.
|
||||
|
||||
Commands:
|
||||
@ -36,6 +37,7 @@ namespace and handling of context variables.
|
||||
calc Raster data calculator.
|
||||
clip Clip a raster to given bounds.
|
||||
convert Copy and convert raster dataset.
|
||||
create Create an empty or filled dataset.
|
||||
edit-info Edit dataset metadata.
|
||||
env Print information about the Rasterio environment.
|
||||
gcps Print ground control points as GeoJSON.
|
||||
@ -52,11 +54,9 @@ namespace and handling of context variables.
|
||||
transform Transform coordinates.
|
||||
warp Warp a raster dataset.
|
||||
|
||||
|
||||
Commands are shown below. See ``--help`` of individual commands for more
|
||||
details.
|
||||
|
||||
|
||||
creation options
|
||||
----------------
|
||||
|
||||
@ -161,7 +161,6 @@ use with, e.g., `geojsonio-cli <https://github.com/mapbox/geojsonio-cli>`__.
|
||||
Shoot the GeoJSON into a Leaflet map using geojsonio-cli by typing
|
||||
``rio bounds tests/data/RGB.byte.tif | geojsonio``.
|
||||
|
||||
|
||||
calc
|
||||
----
|
||||
|
||||
@ -245,6 +244,28 @@ as uint8:
|
||||
|
||||
You can use `--rgb` as shorthand for `--co photometric=rgb`.
|
||||
|
||||
create
|
||||
------
|
||||
|
||||
The ``create`` command creates an empty dataset.
|
||||
|
||||
The fundamental, required parameters are: format driver name, data type, count
|
||||
of bands, height and width in pixels. Long and short options are provided for
|
||||
each of these. Coordinate reference system and affine transformation matrix are
|
||||
not strictly required and have long options only. All other format specific
|
||||
creation outputs must be specified using the --co option.
|
||||
|
||||
The pixel values of an empty dataset are format specific. "Smart" formats like
|
||||
GTiff use 0 or the nodata value if provided.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ rio create new.tif -f GTiff -t uint8 -n 3 -h 512 -w 512 \
|
||||
> --co tiled=true --co blockxsize=256 --co blockysize=256
|
||||
|
||||
The command above produces a 3-band GeoTIFF with 256 x 256 internal tiling.
|
||||
|
||||
edit-info
|
||||
---------
|
||||
|
||||
@ -62,6 +62,7 @@ class NotGeoreferencedWarning(UserWarning):
|
||||
class TransformWarning(UserWarning):
|
||||
"""Warn that coordinate transformations may behave unexpectedly"""
|
||||
|
||||
|
||||
class RPCError(ValueError):
|
||||
"""Raised when RPC transformation is invalid"""
|
||||
|
||||
|
||||
166
rasterio/rio/create.py
Normal file
166
rasterio/rio/create.py
Normal file
@ -0,0 +1,166 @@
|
||||
"""The rio create command."""
|
||||
|
||||
import click
|
||||
import json
|
||||
import os
|
||||
|
||||
import rasterio
|
||||
from rasterio.crs import CRS
|
||||
from rasterio.errors import (
|
||||
CRSError,
|
||||
FileOverwriteError,
|
||||
RasterioIOError,
|
||||
)
|
||||
from rasterio.rio import options
|
||||
from rasterio.transform import Affine, guard_transform
|
||||
|
||||
|
||||
def crs_handler(ctx, param, value):
|
||||
"""Get crs value from the command line."""
|
||||
retval = None
|
||||
if value is not None:
|
||||
try:
|
||||
retval = CRS.from_string(value)
|
||||
except CRSError:
|
||||
raise click.BadParameter(
|
||||
f"{value} is not a recognized CRS.", param=param, param_hint="crs"
|
||||
)
|
||||
return retval
|
||||
|
||||
|
||||
def transform_handler(ctx, param, value):
|
||||
"""Get transform value from the command line."""
|
||||
retval = None
|
||||
if value is not None:
|
||||
try:
|
||||
value = json.loads(value)
|
||||
retval = guard_transform(value)
|
||||
except Exception:
|
||||
raise click.BadParameter(
|
||||
f"{value} is not recognized as a transformarray.",
|
||||
param=param,
|
||||
param_hint="transform",
|
||||
)
|
||||
return retval
|
||||
|
||||
|
||||
@click.command(short_help="Create an empty or filled dataset.")
|
||||
@options.file_out_arg
|
||||
@options.format_opt
|
||||
@options.dtype_opt
|
||||
@click.option("--count", "-n", type=int, help="Number of raster bands.")
|
||||
@click.option("--height", "-h", type=int, help="Raster height, or number of rows.")
|
||||
@click.option("--width", "-w", type=int, help="Raster width, or number of columns.")
|
||||
@options.nodata_opt
|
||||
@click.option(
|
||||
"--crs", callback=crs_handler, default=None, help="Coordinate reference system."
|
||||
)
|
||||
@click.option(
|
||||
"--transform",
|
||||
callback=transform_handler,
|
||||
help="Affine transform matrix. Overrides any given bounds option.",
|
||||
)
|
||||
@options.bounds_opt
|
||||
@options.overwrite_opt
|
||||
@options.creation_options
|
||||
@click.pass_context
|
||||
def create(
|
||||
ctx,
|
||||
output,
|
||||
driver,
|
||||
dtype,
|
||||
count,
|
||||
height,
|
||||
width,
|
||||
nodata,
|
||||
crs,
|
||||
transform,
|
||||
bounds,
|
||||
overwrite,
|
||||
creation_options,
|
||||
):
|
||||
"""Create an empty dataset.
|
||||
|
||||
The fundamental, required parameters are: format driver name, data
|
||||
type, count of bands, height and width in pixels. Long and short
|
||||
options are provided for each of these. Coordinate reference system
|
||||
and affine transformation matrix are not strictly required and have
|
||||
long options only. All other format specific creation outputs must
|
||||
be specified using the --co option.
|
||||
|
||||
Simple north-up, non-rotated georeferencing can be set by using the
|
||||
--bounds option. The --transform option will assign an arbitrarily
|
||||
rotated affine transformation matrix to the dataset. Ground control
|
||||
points, rational polynomial coefficients, and geolocation matrices
|
||||
are not supported.
|
||||
|
||||
The pixel values of an empty dataset are format specific. "Smart"
|
||||
formats like GTiff use 0 or the nodata value if provided.
|
||||
|
||||
Example:
|
||||
|
||||
\b
|
||||
$ rio create new.tif -f GTiff -t uint8 -n 3 -h 512 -w 512 \\
|
||||
> --co tiled=true --co blockxsize=256 --co blockysize=256
|
||||
|
||||
The command above produces a 3-band GeoTIFF with 256 x 256 internal
|
||||
tiling.
|
||||
"""
|
||||
# Preventing rio create from overwriting local and remote files,
|
||||
# objects, and datasets is complicated.
|
||||
if os.path.exists(output):
|
||||
if not overwrite:
|
||||
raise FileOverwriteError(
|
||||
"File exists and won't be overwritten without use of the '--overwrite' option."
|
||||
)
|
||||
else: # Check remote or other non-file output.
|
||||
try:
|
||||
with rasterio.open(output) as dataset:
|
||||
# Dataset exists. May or may not be overwritten.
|
||||
if not overwrite:
|
||||
raise FileOverwriteError(
|
||||
"Dataset exists and won't be overwritten without use of the '--overwrite' option."
|
||||
)
|
||||
except RasterioIOError as exc:
|
||||
# TODO: raise a different exception from rasterio.open() in
|
||||
# this case?
|
||||
if "No such file or directory" in str(exc):
|
||||
pass # Good, output does not exist. Continue with no error.
|
||||
else:
|
||||
# Remote output exists, but is not a rasterio dataset.
|
||||
if not overwrite:
|
||||
raise FileOverwriteError(
|
||||
"Object exists and won't be overwritten without use of the '--overwrite' option."
|
||||
)
|
||||
|
||||
# Prepare the dataset's georeferencing.
|
||||
geo_transform = None
|
||||
|
||||
if bounds:
|
||||
left, bottom, right, top = bounds
|
||||
sx = (right - left) / width
|
||||
sy = (bottom - top) / height
|
||||
geo_transform = Affine.translation(left, top) * Affine.scale(sx, sy)
|
||||
if transform:
|
||||
if geo_transform is not None:
|
||||
click.echo(
|
||||
"--transform value is overriding --bounds value. "
|
||||
"Use only one of these options to avoid this warning.",
|
||||
err=True,
|
||||
)
|
||||
geo_transform = transform
|
||||
|
||||
profile = dict(
|
||||
driver=driver,
|
||||
dtype=dtype,
|
||||
count=count,
|
||||
height=height,
|
||||
width=width,
|
||||
nodata=nodata,
|
||||
crs=crs,
|
||||
transform=geo_transform,
|
||||
**creation_options,
|
||||
)
|
||||
|
||||
with ctx.obj["env"], rasterio.open(output, "w", **profile) as dataset:
|
||||
pass
|
||||
@ -264,10 +264,13 @@ bidx_magic_opt = click.option(
|
||||
multiple=True,
|
||||
help="Indexes of input file bands.")
|
||||
|
||||
# TODO: may be better suited to cligj
|
||||
# TODO: may be better suited to cligj?
|
||||
bounds_opt = click.option(
|
||||
'--bounds', default=None, callback=bounds_handler,
|
||||
help='Bounds: "left bottom right top" or "[left, bottom, right, top]".')
|
||||
"--bounds",
|
||||
default=None,
|
||||
callback=bounds_handler,
|
||||
help="Bounds: 'left bottom right top' or '[left, bottom, right, top]'.",
|
||||
)
|
||||
|
||||
dimensions_opt = click.option(
|
||||
'--dimensions',
|
||||
@ -370,5 +373,5 @@ sequence_opt = click.option(
|
||||
"feature collection object.")
|
||||
|
||||
format_opt = click.option(
|
||||
'-f', '--format', '--driver', 'driver',
|
||||
help="Output format driver")
|
||||
"-f", "--format", "--driver", "driver", help="Output format driver."
|
||||
)
|
||||
|
||||
@ -33,6 +33,7 @@ rasterio.rio_commands =
|
||||
calc = rasterio.rio.calc:calc
|
||||
clip = rasterio.rio.clip:clip
|
||||
convert = rasterio.rio.convert:convert
|
||||
create = rasterio.rio.create:create
|
||||
edit-info = rasterio.rio.edit_info:edit
|
||||
env = rasterio.rio.env:env
|
||||
gcps = rasterio.rio.gcps:gcps
|
||||
|
||||
490
tests/test_rio_create.py
Normal file
490
tests/test_rio_create.py
Normal file
@ -0,0 +1,490 @@
|
||||
"""Tests of rio create."""
|
||||
|
||||
import rasterio
|
||||
from rasterio.crs import CRS
|
||||
from rasterio.io import MemoryFile
|
||||
from rasterio.rio.main import main_group
|
||||
|
||||
|
||||
def test_create_bad_transform(runner):
|
||||
"""Raise BadParameter when transform is invalid."""
|
||||
result = runner.invoke(
|
||||
main_group,
|
||||
[
|
||||
"create",
|
||||
"--transform",
|
||||
"lol",
|
||||
"test.tif",
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 2
|
||||
|
||||
|
||||
def test_create_bad_crs(runner):
|
||||
"""Raise BadParameter when CRS is invalid."""
|
||||
result = runner.invoke(
|
||||
main_group,
|
||||
[
|
||||
"create",
|
||||
"--crs",
|
||||
"lol",
|
||||
"test.tif",
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 2
|
||||
|
||||
|
||||
def test_create_bad_nodata(runner):
|
||||
"""Raise BadParameter when nodata is invalid."""
|
||||
result = runner.invoke(
|
||||
main_group,
|
||||
[
|
||||
"create",
|
||||
"--nodata",
|
||||
"lol",
|
||||
"test.tif",
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 2
|
||||
|
||||
|
||||
def test_create_empty(tmp_path, runner):
|
||||
"""Create a new empty tif."""
|
||||
outfile = str(tmp_path.joinpath("out.tif"))
|
||||
result = runner.invoke(
|
||||
main_group,
|
||||
[
|
||||
"create",
|
||||
"--format",
|
||||
"GTiff",
|
||||
"--dtype",
|
||||
"uint8",
|
||||
"--count",
|
||||
"3",
|
||||
"--height",
|
||||
"512",
|
||||
"--width",
|
||||
"256",
|
||||
"--crs",
|
||||
"EPSG:32618",
|
||||
"--transform",
|
||||
"[300.0, 0.0, 101985.0, 0.0, -300.0, 2826915.0]",
|
||||
outfile,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
|
||||
with rasterio.open(outfile) as dataset:
|
||||
assert dataset.shape == (512, 256)
|
||||
assert dataset.count == 3
|
||||
assert dataset.dtypes == ("uint8", "uint8", "uint8")
|
||||
assert dataset.driver == "GTiff"
|
||||
assert dataset.crs == CRS.from_epsg(32618)
|
||||
assert dataset.res == (300.0, 300.0)
|
||||
assert dataset.transform.xoff == 101985.0
|
||||
assert dataset.transform.yoff == 2826915.0
|
||||
assert (dataset.read() == 0).all()
|
||||
|
||||
|
||||
def test_create_bounds(tmp_path, runner):
|
||||
"""Create a new empty tif with a bounding box."""
|
||||
outfile = str(tmp_path.joinpath("out.tif"))
|
||||
result = runner.invoke(
|
||||
main_group,
|
||||
[
|
||||
"create",
|
||||
"--format",
|
||||
"GTiff",
|
||||
"--dtype",
|
||||
"uint8",
|
||||
"--count",
|
||||
"3",
|
||||
"--height",
|
||||
"512",
|
||||
"--width",
|
||||
"256",
|
||||
"--crs",
|
||||
"EPSG:32618",
|
||||
"--bounds",
|
||||
",".join(
|
||||
(
|
||||
str(x)
|
||||
for x in [
|
||||
101985.0,
|
||||
2826915.0 - 512 * 300.0,
|
||||
101985.0 + 256 * 300.0,
|
||||
2826915.0,
|
||||
]
|
||||
)
|
||||
),
|
||||
outfile,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
|
||||
with rasterio.open(outfile) as dataset:
|
||||
assert dataset.shape == (512, 256)
|
||||
assert dataset.count == 3
|
||||
assert dataset.dtypes == ("uint8", "uint8", "uint8")
|
||||
assert dataset.driver == "GTiff"
|
||||
assert dataset.crs == CRS.from_epsg(32618)
|
||||
assert dataset.res == (300.0, 300.0)
|
||||
assert dataset.transform.xoff == 101985.0
|
||||
assert dataset.transform.yoff == 2826915.0
|
||||
assert (dataset.read() == 0).all()
|
||||
|
||||
|
||||
def test_create_override_warning(tmp_path, runner):
|
||||
"""Warn if both --bounds and --transform are used."""
|
||||
outfile = str(tmp_path.joinpath("out.tif"))
|
||||
result = runner.invoke(
|
||||
main_group,
|
||||
[
|
||||
"create",
|
||||
"--format",
|
||||
"GTiff",
|
||||
"--dtype",
|
||||
"uint8",
|
||||
"--count",
|
||||
"3",
|
||||
"--height",
|
||||
"512",
|
||||
"--width",
|
||||
"256",
|
||||
"--crs",
|
||||
"EPSG:32618",
|
||||
"--transform",
|
||||
"[300.0, 0.0, 101985.0, 0.0, -300.0, 2826915.0]",
|
||||
"--bounds",
|
||||
"0,0,1,1",
|
||||
outfile,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
assert "Use only one" in result.output
|
||||
|
||||
with rasterio.open(outfile) as dataset:
|
||||
assert dataset.shape == (512, 256)
|
||||
assert dataset.count == 3
|
||||
assert dataset.dtypes == ("uint8", "uint8", "uint8")
|
||||
assert dataset.driver == "GTiff"
|
||||
assert dataset.crs == CRS.from_epsg(32618)
|
||||
assert dataset.res == (300.0, 300.0)
|
||||
assert dataset.transform.xoff == 101985.0
|
||||
assert dataset.transform.yoff == 2826915.0
|
||||
assert (dataset.read() == 0).all()
|
||||
|
||||
|
||||
def test_create_bounds(tmp_path, runner):
|
||||
"""Create a new empty tif with a bounding box."""
|
||||
outfile = str(tmp_path.joinpath("out.tif"))
|
||||
result = runner.invoke(
|
||||
main_group,
|
||||
[
|
||||
"create",
|
||||
"--format",
|
||||
"GTiff",
|
||||
"--dtype",
|
||||
"uint8",
|
||||
"--count",
|
||||
"3",
|
||||
"--height",
|
||||
"512",
|
||||
"--width",
|
||||
"256",
|
||||
"--crs",
|
||||
"EPSG:32618",
|
||||
"--bounds",
|
||||
",".join(
|
||||
(
|
||||
str(x)
|
||||
for x in [
|
||||
101985.0,
|
||||
2826915.0 - 512 * 300.0,
|
||||
101985.0 + 256 * 300.0,
|
||||
2826915.0,
|
||||
]
|
||||
)
|
||||
),
|
||||
outfile,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
|
||||
with rasterio.open(outfile) as dataset:
|
||||
assert dataset.shape == (512, 256)
|
||||
assert dataset.count == 3
|
||||
assert dataset.dtypes == ("uint8", "uint8", "uint8")
|
||||
assert dataset.driver == "GTiff"
|
||||
assert dataset.crs == CRS.from_epsg(32618)
|
||||
assert dataset.res == (300.0, 300.0)
|
||||
assert dataset.transform.xoff == 101985.0
|
||||
assert dataset.transform.yoff == 2826915.0
|
||||
assert (dataset.read() == 0).all()
|
||||
|
||||
def test_create_short_opts(tmp_path, runner):
|
||||
"""Create a new empty tif using short options."""
|
||||
outfile = str(tmp_path.joinpath("out.tif"))
|
||||
result = runner.invoke(
|
||||
main_group,
|
||||
[
|
||||
"create",
|
||||
"-f",
|
||||
"GTiff",
|
||||
"-t",
|
||||
"uint8",
|
||||
"-n",
|
||||
"3",
|
||||
"-h",
|
||||
"512",
|
||||
"-w",
|
||||
"256",
|
||||
outfile,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
|
||||
with rasterio.open(outfile) as dataset:
|
||||
assert dataset.shape == (512, 256)
|
||||
assert dataset.count == 3
|
||||
assert dataset.dtypes == ("uint8", "uint8", "uint8")
|
||||
assert dataset.driver == "GTiff"
|
||||
assert (dataset.read() == 0).all()
|
||||
|
||||
|
||||
def test_create_nodata(tmp_path, runner):
|
||||
"""Create a new tif with no valid data."""
|
||||
outfile = str(tmp_path.joinpath("out.tif"))
|
||||
result = runner.invoke(
|
||||
main_group,
|
||||
[
|
||||
"create",
|
||||
"-f",
|
||||
"GTiff",
|
||||
"-t",
|
||||
"uint8",
|
||||
"-n",
|
||||
"3",
|
||||
"-h",
|
||||
"512",
|
||||
"-w",
|
||||
"256",
|
||||
"--nodata",
|
||||
"255",
|
||||
outfile,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
|
||||
with rasterio.open(outfile) as dataset:
|
||||
assert dataset.shape == (512, 256)
|
||||
assert dataset.count == 3
|
||||
assert dataset.dtypes == ("uint8", "uint8", "uint8")
|
||||
assert dataset.nodatavals == (255, 255, 255)
|
||||
assert dataset.driver == "GTiff"
|
||||
raster = dataset.read(masked=True)
|
||||
assert (raster.data == 255).all()
|
||||
assert raster.mask.all()
|
||||
|
||||
|
||||
def test_create_creation_opts(tmp_path, runner):
|
||||
"""Create a new tif with creation/opening options."""
|
||||
outfile = str(tmp_path.joinpath("out.tif"))
|
||||
result = runner.invoke(
|
||||
main_group,
|
||||
[
|
||||
"create",
|
||||
"-f",
|
||||
"GTiff",
|
||||
"-t",
|
||||
"uint8",
|
||||
"-n",
|
||||
"3",
|
||||
"-h",
|
||||
"512",
|
||||
"-w",
|
||||
"256",
|
||||
"--co",
|
||||
"tiled=true",
|
||||
"--co",
|
||||
"blockxsize=128",
|
||||
"--co",
|
||||
"blockysize=256",
|
||||
outfile,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
|
||||
with rasterio.open(outfile) as dataset:
|
||||
assert dataset.shape == (512, 256)
|
||||
assert dataset.count == 3
|
||||
assert dataset.dtypes == ("uint8", "uint8", "uint8")
|
||||
assert dataset.driver == "GTiff"
|
||||
assert all(((256, 128) == hw for hw in dataset.block_shapes))
|
||||
|
||||
|
||||
def test_create_no_overwrite(tmp_path, runner):
|
||||
"""Don't allow overwrite of existing file without option."""
|
||||
outpath = tmp_path.joinpath("out.tif")
|
||||
outpath.touch()
|
||||
outfile = str(outpath)
|
||||
|
||||
result = runner.invoke(
|
||||
main_group,
|
||||
[
|
||||
"create",
|
||||
"-f",
|
||||
"GTiff",
|
||||
"-t",
|
||||
"uint8",
|
||||
"-n",
|
||||
"3",
|
||||
"-h",
|
||||
"512",
|
||||
"-w",
|
||||
"256",
|
||||
outfile,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 1
|
||||
assert "File exists and won't be overwritten" in result.output
|
||||
|
||||
|
||||
def test_create_overwrite(tmp_path, runner):
|
||||
"""Allow overwrite of existing file with option."""
|
||||
outpath = tmp_path.joinpath("out.tif")
|
||||
outpath.touch()
|
||||
outfile = str(outpath)
|
||||
|
||||
result = runner.invoke(
|
||||
main_group,
|
||||
[
|
||||
"create",
|
||||
"-f",
|
||||
"GTiff",
|
||||
"-t",
|
||||
"uint8",
|
||||
"-n",
|
||||
"3",
|
||||
"-h",
|
||||
"512",
|
||||
"-w",
|
||||
"256",
|
||||
"--overwrite",
|
||||
outfile,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
|
||||
|
||||
def test_create_no_overwrite_nonfile(runner):
|
||||
"""Don't allow overwrite of existing non-file without option."""
|
||||
with MemoryFile(bytes(bytearray(100000))) as memfile:
|
||||
result = runner.invoke(
|
||||
main_group,
|
||||
[
|
||||
"create",
|
||||
"-f",
|
||||
"GTiff",
|
||||
"-t",
|
||||
"uint8",
|
||||
"-n",
|
||||
"3",
|
||||
"-h",
|
||||
"512",
|
||||
"-w",
|
||||
"256",
|
||||
memfile.name,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 1
|
||||
assert "Object exists and won't be overwritten" in result.output
|
||||
|
||||
|
||||
def test_create_overwrite_nonfile(runner):
|
||||
"""Allow overwrite of existing non-file with option."""
|
||||
with MemoryFile(bytes(bytearray(100000))) as memfile:
|
||||
result = runner.invoke(
|
||||
main_group,
|
||||
[
|
||||
"create",
|
||||
"-f",
|
||||
"GTiff",
|
||||
"-t",
|
||||
"uint8",
|
||||
"-n",
|
||||
"1",
|
||||
"-h",
|
||||
"16",
|
||||
"-w",
|
||||
"16",
|
||||
"--overwrite",
|
||||
memfile.name,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
|
||||
with rasterio.open(memfile.name) as dataset:
|
||||
assert dataset.count == 1
|
||||
assert dataset.height == 16
|
||||
assert dataset.width == 16
|
||||
|
||||
|
||||
def test_create_no_overwrite_nonfile_2(path_rgb_byte_tif, runner):
|
||||
"""Don't allow overwrite of existing non-file dataset without option."""
|
||||
with open(path_rgb_byte_tif, "rb") as dataset:
|
||||
data = dataset.read()
|
||||
|
||||
with MemoryFile(data) as memfile:
|
||||
result = runner.invoke(
|
||||
main_group,
|
||||
[
|
||||
"create",
|
||||
"-f",
|
||||
"GTiff",
|
||||
"-t",
|
||||
"uint8",
|
||||
"-n",
|
||||
"3",
|
||||
"-h",
|
||||
"512",
|
||||
"-w",
|
||||
"256",
|
||||
memfile.name,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 1
|
||||
assert "Dataset exists and won't be overwritten" in result.output
|
||||
assert memfile.read(1024) == data[:1024]
|
||||
|
||||
|
||||
def test_create_overwrite_nonfile_2(path_rgb_byte_tif, runner):
|
||||
"""Allow overwrite of existing non-file dataset with option."""
|
||||
with open(path_rgb_byte_tif, "rb") as dataset:
|
||||
data = dataset.read()
|
||||
|
||||
with MemoryFile(data) as memfile:
|
||||
result = runner.invoke(
|
||||
main_group,
|
||||
[
|
||||
"create",
|
||||
"-f",
|
||||
"GTiff",
|
||||
"-t",
|
||||
"uint8",
|
||||
"-n",
|
||||
"1",
|
||||
"-h",
|
||||
"512",
|
||||
"-w",
|
||||
"256",
|
||||
"--overwrite",
|
||||
memfile.name,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
|
||||
with rasterio.open(memfile.name) as dataset:
|
||||
assert dataset.count == 1
|
||||
assert dataset.height == 512
|
||||
assert dataset.width == 256
|
||||
@ -19,16 +19,14 @@ def bbox(*args):
|
||||
|
||||
|
||||
def test_shapes(runner, pixelated_image_file):
|
||||
with pytest.warns(None):
|
||||
|
||||
result = runner.invoke(main_group, ['shapes', '--collection', pixelated_image_file])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert result.output.count('"FeatureCollection"') == 1
|
||||
assert result.output.count('"Feature"') == 4
|
||||
assert np.allclose(
|
||||
json.loads(result.output)['features'][0]['geometry']['coordinates'],
|
||||
[[[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]])
|
||||
result = runner.invoke(main_group, ["shapes", "--collection", pixelated_image_file])
|
||||
assert result.exit_code == 0
|
||||
assert result.output.count('"FeatureCollection"') == 1
|
||||
assert result.output.count('"Feature"') == 4
|
||||
assert np.allclose(
|
||||
json.loads(result.output)["features"][0]["geometry"]["coordinates"],
|
||||
[[[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]],
|
||||
)
|
||||
|
||||
|
||||
def test_shapes_invalid_bidx(runner, pixelated_image_file):
|
||||
@ -44,15 +42,14 @@ def test_shapes_sequence(runner, pixelated_image_file):
|
||||
--sequence option should produce 4 features in series rather than
|
||||
inside a feature collection.
|
||||
"""
|
||||
with pytest.warns(None):
|
||||
result = runner.invoke(
|
||||
main_group, ["shapes", "--collection", pixelated_image_file, "--sequence"]
|
||||
)
|
||||
|
||||
result = runner.invoke(
|
||||
main_group, ['shapes', '--collection', pixelated_image_file, '--sequence'])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert result.output.count('"FeatureCollection"') == 0
|
||||
assert result.output.count('"Feature"') == 4
|
||||
assert result.output.count('\n') == 4
|
||||
assert result.exit_code == 0
|
||||
assert result.output.count('"FeatureCollection"') == 0
|
||||
assert result.output.count('"Feature"') == 4
|
||||
assert result.output.count("\n") == 4
|
||||
|
||||
|
||||
def test_shapes_sequence_rs(runner, pixelated_image_file):
|
||||
@ -88,29 +85,27 @@ def test_shapes_indent(runner, pixelated_image_file):
|
||||
"""
|
||||
--indent option should produce lots of newlines and contiguous spaces
|
||||
"""
|
||||
with pytest.warns(None):
|
||||
result = runner.invoke(
|
||||
main_group, ["shapes", "--collection", pixelated_image_file, "--indent", 2]
|
||||
)
|
||||
|
||||
result = runner.invoke(
|
||||
main_group, ['shapes', '--collection', pixelated_image_file, '--indent', 2])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert result.output.count('"FeatureCollection"') == 1
|
||||
assert result.output.count('"Feature"') == 4
|
||||
assert result.output.count("\n") > 100
|
||||
assert result.output.count(" ") > 100
|
||||
assert result.exit_code == 0
|
||||
assert result.output.count('"FeatureCollection"') == 1
|
||||
assert result.output.count('"Feature"') == 4
|
||||
assert result.output.count("\n") > 100
|
||||
assert result.output.count(" ") > 100
|
||||
|
||||
|
||||
def test_shapes_compact(runner, pixelated_image_file):
|
||||
with pytest.warns(None):
|
||||
result = runner.invoke(
|
||||
main_group, ["shapes", "--collection", pixelated_image_file, "--compact"]
|
||||
)
|
||||
|
||||
result = runner.invoke(
|
||||
main_group, ['shapes', '--collection', pixelated_image_file, '--compact'])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert result.output.count('"FeatureCollection"') == 1
|
||||
assert result.output.count('"Feature"') == 4
|
||||
assert result.output.count(', ') == 0
|
||||
assert result.output.count(': ') == 0
|
||||
assert result.exit_code == 0
|
||||
assert result.output.count('"FeatureCollection"') == 1
|
||||
assert result.output.count('"Feature"') == 4
|
||||
assert result.output.count(", ") == 0
|
||||
assert result.output.count(": ") == 0
|
||||
|
||||
|
||||
def test_shapes_sampling(runner, pixelated_image_file):
|
||||
@ -147,13 +142,13 @@ def test_shapes_mask(runner, pixelated_image, pixelated_image_file):
|
||||
with rasterio.open(pixelated_image_file, 'r+') as out:
|
||||
out.write(pixelated_image, indexes=1)
|
||||
|
||||
with pytest.warns(None):
|
||||
result = runner.invoke(
|
||||
main_group, ['shapes', '--collection', pixelated_image_file, '--mask'])
|
||||
assert result.exit_code == 0
|
||||
assert result.output.count('"FeatureCollection"') == 1
|
||||
assert result.output.count('"Feature"') == 1
|
||||
assert shape(json.loads(result.output)["features"][0]["geometry"]).area == 31.0
|
||||
result = runner.invoke(
|
||||
main_group, ["shapes", "--collection", pixelated_image_file, "--mask"]
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
assert result.output.count('"FeatureCollection"') == 1
|
||||
assert result.output.count('"Feature"') == 1
|
||||
assert shape(json.loads(result.output)["features"][0]["geometry"]).area == 31.0
|
||||
|
||||
|
||||
def test_shapes_mask_sampling(runner, pixelated_image, pixelated_image_file):
|
||||
@ -170,16 +165,15 @@ def test_shapes_mask_sampling(runner, pixelated_image, pixelated_image_file):
|
||||
with rasterio.open(pixelated_image_file, 'r+') as out:
|
||||
out.write(pixelated_image, indexes=1)
|
||||
|
||||
with pytest.warns(None):
|
||||
result = runner.invoke(
|
||||
main_group,
|
||||
["shapes", "--collection", pixelated_image_file, "--mask", "--sampling", 5],
|
||||
)
|
||||
|
||||
result = runner.invoke(
|
||||
main_group,
|
||||
['shapes', '--collection', pixelated_image_file, '--mask', '--sampling', 5])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert result.output.count('"FeatureCollection"') == 1
|
||||
assert result.output.count('"Feature"') == 1
|
||||
assert shape(json.loads(result.output)["features"][0]["geometry"]).area == 25.0
|
||||
assert result.exit_code == 0
|
||||
assert result.output.count('"FeatureCollection"') == 1
|
||||
assert result.output.count('"Feature"') == 1
|
||||
assert shape(json.loads(result.output)["features"][0]["geometry"]).area == 25.0
|
||||
|
||||
|
||||
def test_shapes_band1_as_mask(runner, pixelated_image, pixelated_image_file):
|
||||
@ -195,12 +189,20 @@ def test_shapes_band1_as_mask(runner, pixelated_image, pixelated_image_file):
|
||||
with rasterio.open(pixelated_image_file, 'r+') as out:
|
||||
out.write(pixelated_image, indexes=1)
|
||||
|
||||
with pytest.warns(None):
|
||||
result = runner.invoke(
|
||||
main_group,
|
||||
['shapes', '--collection', pixelated_image_file, '--band', '--bidx', '1', '--as-mask'])
|
||||
result = runner.invoke(
|
||||
main_group,
|
||||
[
|
||||
"shapes",
|
||||
"--collection",
|
||||
pixelated_image_file,
|
||||
"--band",
|
||||
"--bidx",
|
||||
"1",
|
||||
"--as-mask",
|
||||
],
|
||||
)
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert result.output.count('"FeatureCollection"') == 1
|
||||
assert result.output.count('"Feature"') == 3
|
||||
assert shape(json.loads(result.output)["features"][0]["geometry"]).area == 1.0
|
||||
assert result.exit_code == 0
|
||||
assert result.output.count('"FeatureCollection"') == 1
|
||||
assert result.output.count('"Feature"') == 3
|
||||
assert shape(json.loads(result.output)["features"][0]["geometry"]).area == 1.0
|
||||
|
||||
@ -36,8 +36,5 @@ def test_no_notgeoref_warning(transform, gcps, rpcs):
|
||||
if rpcs:
|
||||
src.rpcs = rpcs
|
||||
|
||||
with pytest.warns(None) as record:
|
||||
with mem.open() as dst:
|
||||
pass
|
||||
|
||||
assert len(record) == 0
|
||||
with mem.open() as dst:
|
||||
pass
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user