diff --git a/.circleci/config.yml b/.circleci/config.yml index d2d1b1d..f260b3b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,7 +4,6 @@ common: &common working_directory: ~/raster-tester steps: - checkout - - run: sudo apt-get -y --no-install-recommends --force-yes install gdal-bin libgdal-dev - run: sudo pip install virtualenv - run: virtualenv venv - run: @@ -20,6 +19,7 @@ common: &common ~/.local/bin/coverage xml ~/.local/bin/codecov fi + when: always jobs: "python-2.7": diff --git a/raster_tester/aligned.py b/raster_tester/aligned.py index bf16926..bb2b10d 100644 --- a/raster_tester/aligned.py +++ b/raster_tester/aligned.py @@ -1,6 +1,6 @@ -#!/usr/bin/env python import rasterio + def tiled(sources, equal_blocks=True): """ Tests if raster sources have the same tiling optionally assert that their block sizes are equal (default: True) @@ -10,7 +10,7 @@ def tiled(sources, equal_blocks=True): for source in sources: with rasterio.open(source) as src: - if not src.profile['tiled']: + if not src.is_tiled: return (False, "Source(s) are not internally tiled") if equal_blocks: @@ -37,10 +37,10 @@ def aligned(sources): with rasterio.open(source) as src: if atransform: - if src.affine != atransform: + if src.transform != atransform: return (False, "Affine transforms are not aligned") else: - atransform = src.affine + atransform = src.transform if shape: if src.shape != shape: diff --git a/raster_tester/compare.py b/raster_tester/compare.py index 5267d1c..452577b 100644 --- a/raster_tester/compare.py +++ b/raster_tester/compare.py @@ -1,18 +1,14 @@ -#!/usr/bin/env python -from affine import Affine import click import numpy as np + +from affine import Affine + import rasterio +from rasterio.crs import CRS from rasterio.warp import reproject -try: - from rasterio.crs import CRS -except: - CRS = None -try: - from rasterio.warp import RESAMPLING -except ImportError: - from rasterio.enums import Resampling as RESAMPLING + +from rasterio.enums import Resampling as RESAMPLING from rasterio.coords import BoundingBox from .utils import exception_raiser @@ -116,13 +112,15 @@ def compare(srcpath1, srcpath2, max_px_diff=0, upsample=1, downsample=1, "In flex mode, %s and %s must 3 and 4, or 4 and 3 bands " "respectively (received %s and %s)" % ( srcpath1, srcpath2, src1.count, src2.count), no_stderr) + return else: props = ['count', 'crs', 'dtypes', 'driver', 'bounds', - 'height', 'width', 'shape', 'nodatavals'] + 'height', 'width', 'shape', 'nodatavals'] propCompare = compare_properties(src1, src2, props) if propCompare: exception_raiser(propCompare, no_stderr) + return if compare_masked and src1.count == 4 and not flex_mode: # create arrays for decimated reading @@ -140,6 +138,7 @@ def compare(srcpath1, srcpath2, max_px_diff=0, upsample=1, downsample=1, if aboveThreshold: exception_raiser( 'Mask has %s pixels that vary by more than 16' % (difference), no_stderr) + return elif compare_masked and flex_mode: masked_1 = make_fill_array( @@ -158,6 +157,7 @@ def compare(srcpath1, srcpath2, max_px_diff=0, upsample=1, downsample=1, exception_raiser( 'Mask has %s pixels that vary by more than 16' % (difference), no_stderr) + return for bidx in range(1, count1 + compareAlpha): # create arrays for decimated reading @@ -187,6 +187,7 @@ def compare(srcpath1, srcpath2, max_px_diff=0, upsample=1, downsample=1, if aboveThreshold: exception_raiser('Band %s has %s pixels that vary by more than 16' % ( bidx, difference), no_stderr) + return click.echo("ok - %s is similar to within %s pixels of %s" % (srcpath1, max_px_diff, srcpath2)) diff --git a/raster_tester/crosses_dateline.py b/raster_tester/crosses_dateline.py index 42492b5..0ba3a20 100644 --- a/raster_tester/crosses_dateline.py +++ b/raster_tester/crosses_dateline.py @@ -1,6 +1,8 @@ + +import numpy as np + import rasterio as rio from rasterio import warp -import numpy as np def _wrap_x_coord(xcoords): ''' @@ -18,6 +20,7 @@ def _wrap_x_coord(xcoords): ''' return ((xcoords + 180) % 360) - 180 + def winding_order(boundsArr): ''' returns False if CCW; True is CW diff --git a/raster_tester/empty.py b/raster_tester/empty.py index a45b5ca..c5c6ed3 100644 --- a/raster_tester/empty.py +++ b/raster_tester/empty.py @@ -1,11 +1,12 @@ -import click, sys import numpy as np import rasterio as rio + def array_hasdata(arr): return np.any(arr) + def is_empty(input_path, randomize, bidx): with rio.open(input_path) as src: windows = [window for ij, window in src.block_windows()] diff --git a/raster_tester/scripts/cli.py b/raster_tester/scripts/cli.py index 9f8b69e..a483da3 100644 --- a/raster_tester/scripts/cli.py +++ b/raster_tester/scripts/cli.py @@ -1,6 +1,7 @@ -import click + import sys +import click import raster_tester @@ -9,7 +10,7 @@ def cli(): pass -@click.command("compare") +@cli.command("compare") @click.argument("input_1", type=click.Path(exists=True)) @click.argument("input_2", type=click.Path(exists=True)) @click.option("--pixel-threshold", "-p", type=int, default=0, @@ -33,10 +34,7 @@ def compare(input_1, input_2, pixel_threshold, upsample, downsample, compare_masked, no_error, debug, flex_mode) -cli.add_command(compare) - - -@click.command("isempty") +@cli.command("isempty") @click.argument("input_1", type=click.Path(exists=True)) @click.option('--bidx', '-b', default=4, help="Bands to blob [default = 4]") @@ -44,34 +42,23 @@ def compare(input_1, input_2, pixel_threshold, upsample, downsample, help='iterate through windows in a psuedorandom fashion') def isempty(input_1, randomize, bidx): empty = raster_tester.is_empty(input_1, randomize, bidx, ) - exits = { - True: ("is empty", 0), - False: ("is not empty", 1) - } - - message, eCode = exits[empty] - - click.echo("%s %s" % (input_1, message)) - sys.exit(eCode) - -cli.add_command(isempty) + if empty: + click.echo('{} is empty'.format(input_1)) + else: + raise click.ClickException("{} is not empty".format(input_1)) -@click.command("isaligned") +@cli.command("isaligned") @click.argument('sources', required=True, nargs=-1) def isaligned(sources): aligned, msg = raster_tester.aligned(sources) if aligned: click.echo("ok: {} are aligned ({})".format(', '.join(sources), msg)) - sys.exit(0) else: - click.echo("not ok: {} are not aligned ({})".format(', '.join(sources), msg)) - sys.exit(1) + raise click.ClickException("not ok: {} are not aligned ({})".format(', '.join(sources), msg)) -cli.add_command(isaligned) - -@click.command("istiled") +@cli.command("istiled") @click.argument('sources', required=True, nargs=-1) @click.option('--blocksize/--no-blocksize', is_flag=True, default=True, help="assert that sources are internally tiled") @@ -79,25 +66,15 @@ def istiled(sources, blocksize): result, msg = raster_tester.tiled(sources, blocksize) if result: click.echo("ok: {} are tiled ({})".format(', '.join(sources), msg)) - sys.exit(0) else: - click.echo("not ok: {} are not all tiled ({})".format(', '.join(sources), msg)) - sys.exit(1) + raise click.ClickException("not ok: {} are not all tiled ({})".format(', '.join(sources), msg)) -cli.add_command(istiled) -@click.command('crossesdateline') +@cli.command('crossesdateline') @click.argument('input', type=click.Path(exists=True)) def crossesdateline(input): result = raster_tester.crosses_dateline(input) if result: - click.echo('%s crosses dateline; exit 1' % (input)) - sys.exit(1) + raise click.ClickException('{} crosses dateline; exit 1'.format(input)) else: - click.echo('%s does not cross dateline; exit 0' % (input)) - sys.exit(0) - -cli.add_command(crossesdateline) - -if __name__ == "__main__": - cli() + click.echo('{} does not cross dateline; exit 0'.format(input)) diff --git a/raster_tester/utils.py b/raster_tester/utils.py index 61cfc57..a1d9f4a 100644 --- a/raster_tester/utils.py +++ b/raster_tester/utils.py @@ -1,11 +1,9 @@ -#!/usr/bin/env python -import sys import click + def exception_raiser(message, no_stderr): if no_stderr: click.echo("not ok - %s" % (message)) - sys.exit(0) else: - raise ValueError(message) + raise click.ClickException("not ok - {}".format(message)) diff --git a/requirements.txt b/requirements.txt index 089b9ab..3b533a6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ click -rasterio \ No newline at end of file +rasterio>=1.0b1 diff --git a/setup.py b/setup.py index 22b4a3e..dd70c7d 100644 --- a/setup.py +++ b/setup.py @@ -8,10 +8,14 @@ setup(name='raster-tester', - version='0.10.0', + version='0.11.0', description=u"Tools for testing rasters", long_description=long_description, - classifiers=[], + classifiers=[ + 'Topic :: Scientific/Engineering :: GIS', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.6' + ], keywords='', author=u"Camilla Mahon", author_email='camilla@mapbox.com', diff --git a/tests/test_aligned.py b/tests/test_aligned.py index 1122293..fa59f89 100644 --- a/tests/test_aligned.py +++ b/tests/test_aligned.py @@ -1,4 +1,6 @@ + from click.testing import CliRunner + from raster_tester.scripts.cli import cli import raster_tester diff --git a/tests/test_cli.py b/tests/test_cli.py index 37b987d..780a4eb 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,4 +1,3 @@ -import os, shutil from click.testing import CliRunner from raster_tester.scripts.cli import cli @@ -10,6 +9,7 @@ def make_fake(path, empty): + """Create a fake dataset.""" crOptions = { 'count': 4, 'crs': {'init': u'epsg:3857'}, @@ -35,51 +35,79 @@ def make_fake(path, empty): def test_cli_okcompare(): + """Shoult exit 0: rasters are equals.""" runner = CliRunner() - result = runner.invoke(cli, ['compare', 'tests/expected/blobby.tif', 'tests/fixtures/notblobby.tif', '--upsample', '8', '--compare-masked', '--downsample', '64']) + result = runner.invoke(cli, ['compare', 'tests/expected/blobby.tif', + 'tests/fixtures/notblobby.tif', + '--upsample', '8', + '--compare-masked', '--downsample', '64']) assert result.exit_code == 0 def test_cli_okcompare_bad(): + """Shoult exit 1: rasters are different.""" runner = CliRunner() - result = runner.invoke(cli, ['compare', 'tests/expected/blobby.tif', 'tests/fixtures/notblobby.tif', '--upsample', '8', '--downsample', '64']) - assert result.exit_code == -1 + result = runner.invoke(cli, ['compare', 'tests/expected/blobby.tif', + 'tests/fixtures/notblobby.tif']) + assert result.exit_code == 1 + assert result.output == 'Error: not ok - Band 1 has 4356 pixels that vary by more than 16\n' def test_cli_okcompare_bad_no_error(): + """Shoult exit 0: raster are different but `--no-error` is set to True.""" runner = CliRunner() - result = runner.invoke(cli, ['compare', 'tests/expected/blobby.tif', 'tests/fixtures/notblobby.tif', '--upsample', '8', '--downsample', '64', '--no-error']) + result = runner.invoke(cli, ['compare', 'tests/expected/blobby.tif', + 'tests/fixtures/notblobby.tif', '--no-error']) assert result.exit_code == 0 - assert result.output == 'not ok - Band 1 has 363 pixels that vary by more than 16\n' + assert result.output == 'not ok - Band 1 has 4356 pixels that vary by more than 16\n' def test_cli_okcompare_rgb(): + """Shoult exit 0: rasters are equals.""" runner = CliRunner() - result = runner.invoke(cli, ['compare', 'tests/expected/blobby_rgb.tif', 'tests/fixtures/notblobby_rgb.tif', '--upsample', '8', '--compare-masked', '--downsample', '64', '--flex-mode']) + result = runner.invoke(cli, ['compare', 'tests/expected/blobby_rgb.tif', + 'tests/fixtures/notblobby_rgb.tif', + '--upsample', '8', '--compare-masked', + '--downsample', '64', '--flex-mode']) assert result.exit_code == 0 def test_cli_okcompare_rgb_rev(): + """Shoult exit 0: rasters are equals.""" runner = CliRunner() - result = runner.invoke(cli, ['compare', 'tests/fixtures/notblobby_rgb.tif', 'tests/expected/blobby_rgb.tif', '--upsample', '8', '--compare-masked', '--downsample', '64', '--flex-mode']) + result = runner.invoke(cli, ['compare', 'tests/fixtures/notblobby_rgb.tif', + 'tests/expected/blobby_rgb.tif', + '--upsample', '8', + '--compare-masked', '--downsample', '64', + '--flex-mode']) assert result.exit_code == 0 def test_cli_okcompare_bad_rgb(): + """Shoult exit 1: rasters are different.""" runner = CliRunner() - result = runner.invoke(cli, ['compare', 'tests/expected/blobby_rgb.tif', 'tests/fixtures/notblobby_rgb.tif', '--upsample', '8', '--downsample', '64', '--compare-masked']) - assert result.exit_code == -1 + result = runner.invoke(cli, ['compare', 'tests/expected/blobby_rgb.tif', + 'tests/fixtures/notblobby_rgb.tif', + '--upsample', '8', '--downsample', '64', + '--compare-masked']) + assert result.exit_code == 1 def test_cli_okcompare_bad_rgb_rev(): + """Shoult exit 1: rasters are different.""" runner = CliRunner() - result = runner.invoke(cli, ['compare', 'tests/fixtures/notblobby_rgb.tif', 'tests/expected/blobby_rgb.tif', '--upsample', '8', '--downsample', '64', '--compare-masked']) - assert result.exit_code == -1 + result = runner.invoke(cli, ['compare', 'tests/fixtures/notblobby_rgb.tif', + 'tests/expected/blobby_rgb.tif', + '--upsample', '8', '--downsample', '64', + '--compare-masked']) + assert result.exit_code == 1 def test_isempty(tmpdir): + """Shoult exit 0: raster is empty.""" fakeEmpty = str(tmpdir.join('empty.tif')) make_fake(fakeEmpty, True) + runner = CliRunner() result = runner.invoke(cli, ['isempty', fakeEmpty, '--randomize']) assert result.exit_code == 0 @@ -87,31 +115,38 @@ def test_isempty(tmpdir): def test_isnotempty(tmpdir): + """Shoult exit 1: raster is not empty.""" fakeEmpty = str(tmpdir.join('notempty.tif')) make_fake(fakeEmpty, False) + runner = CliRunner() result = runner.invoke(cli, ['isempty', fakeEmpty, '--randomize']) assert result.exit_code == 1 - assert result.output == "%s is not empty\n" % (fakeEmpty) + assert result.output == "Error: %s is not empty\n" % (fakeEmpty) def test_does_not_cross_dateline(): + """Shoult exit 0: raster does not cross dateline.""" runner = CliRunner() - result = runner.invoke(cli, ['crossesdateline', 'tests/fixtures/not_cross_dateline.tif']) + result = runner.invoke(cli, ['crossesdateline', + 'tests/fixtures/not_cross_dateline.tif']) assert result.exit_code == 0 assert result.output == 'tests/fixtures/not_cross_dateline.tif does not cross dateline; exit 0\n' def test_does_cross_dateline(): + """Shoult exit 1: raster cross dateline.""" runner = CliRunner() - result = runner.invoke(cli, ['crossesdateline', 'tests/fixtures/crosses_dateline.tif']) + result = runner.invoke(cli, ['crossesdateline', + 'tests/fixtures/crosses_dateline.tif']) assert result.exit_code == 1 - assert result.output == 'tests/fixtures/crosses_dateline.tif crosses dateline; exit 1\n' + assert result.output == 'Error: tests/fixtures/crosses_dateline.tif crosses dateline; exit 1\n' def test_does_not_cross_dateline_square(): + """Shoult exit 0: raster does not cross dateline.""" runner = CliRunner() - result = runner.invoke(cli, ['crossesdateline', 'tests/fixtures/not_cross_dateline_square.tif']) + result = runner.invoke(cli, ['crossesdateline', + 'tests/fixtures/not_cross_dateline_square.tif']) assert result.exit_code == 0 - assert result.output == 'tests/fixtures/not_cross_dateline_square.tif does not cross dateline; exit 0\n' diff --git a/tests/test_dateline.py b/tests/test_dateline.py index d41a563..46b66ff 100644 --- a/tests/test_dateline.py +++ b/tests/test_dateline.py @@ -1,4 +1,5 @@ -from raster_tester import make_bounds_array, densify, transform_bounds, winding_order +from raster_tester import (make_bounds_array, densify, + transform_bounds, winding_order) from rasterio.coords import BoundingBox @@ -9,18 +10,17 @@ def test_densification_count(): boundsArr = densify(make_bounds_array(BoundingBox(-120., 45., -115., 50.)), 8) assert boundsArr.shape == (32, 2) - assert winding_order(boundsArr) == False + assert not winding_order(boundsArr) def test_4326_crossing(): crs = {'init': 'epsg:4326'} boundsArr = densify(make_bounds_array(BoundingBox(170., 45., 190., 50.))) - assert winding_order(boundsArr) == False + assert not winding_order(boundsArr) transformedBounds = transform_bounds(boundsArr, crs) - - assert winding_order(transformedBounds) == True + assert winding_order(transformedBounds) def test_4326_not_crossing(): @@ -29,7 +29,7 @@ def test_4326_not_crossing(): boundsArr = densify(make_bounds_array(BoundingBox(-120., 45., -115., 50.))) transformedBounds = transform_bounds(boundsArr, crs) - assert winding_order(transformedBounds) == False + assert not winding_order(transformedBounds) def test_should_fail_with_bad_crs(): diff --git a/tox.ini b/tox.ini index b96d12c..f475b3d 100644 --- a/tox.ini +++ b/tox.ini @@ -1,15 +1,10 @@ -[flake8] -max-line-length = 88 -max-complexity = 10 -inline-quotes = double - [tox] envlist = py27,py36 [testenv] -commands=python -m pytest --cov raster_tester --ignore=venv +extras = test +install_command = pip install {opts} {packages} --only-binary rasterio +commands= + python -m pytest --cov raster_tester --cov-report term-missing --ignore=venv deps= numpy - pytest - pytest-cov - codecov