Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix several issues in geoviewer #418

Merged
merged 19 commits into from
Jan 18, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions bin/geoviewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@

def getparser() -> argparse.ArgumentParser:
# Set up description
parser = argparse.ArgumentParser(description="Visualisation tool for any image supported by GDAL.")
parser = argparse.ArgumentParser(
description="Visualisation tool for any image supported by GDAL. For single band plots (Single band rasters or with option -band) the image will be rendered as a pseudocolor image using the set or default colormap. For 3 or 4 band data, the image will be plotted as an RGB(A) image. For other band counts, an error will be raised and the option -band must be used."
)

# Positional arguments
parser.add_argument("filename", type=str, help="str, path to the image")
Expand All @@ -30,7 +32,7 @@ def getparser() -> argparse.ArgumentParser:
dest="cmap",
type=str,
default="default",
help="str, a matplotlib colormap string (default is from rcParams).",
help="str, a matplotlib colormap string (default is from rcParams). This parameter is ignored for multi-band rasters.",
)
parser.add_argument(
"-vmin",
Expand All @@ -39,7 +41,7 @@ def getparser() -> argparse.ArgumentParser:
default=None,
help=(
"float, the minimum value for colorscale, or can be expressed as a "
"percentile e.g. 5%% (default is calculated min value)."
"percentile e.g. 5%% (default is calculated min value). This parameter is ignored for multi-band rasters."
),
)
parser.add_argument(
Expand All @@ -49,20 +51,20 @@ def getparser() -> argparse.ArgumentParser:
default=None,
help=(
"float, the maximum value for colorscale, or can be expressed as a "
"percentile e.g. 95%% (default is calculated max value)."
"percentile e.g. 95%% (default is calculated max value). This parameter is ignored for multi-band rasters."
),
)
parser.add_argument(
"-band",
dest="band",
type=int,
default=None,
help="int, which band to display (start at 0) for multiband images (Default is 0).",
help="int, for multiband images, which band to display. Starts at 1. (Default is to load all bands and display as rasterio, i.e. asuming RGB(A) and with clipping outside [0-255] for int, [0-1] for float).",
)
parser.add_argument(
"-nocb",
dest="nocb",
help="If set, will not display a colorbar (Default is to display the colorbar).",
help="If set, will not display a colorbar (Default is to display the colorbar for single-band raster). This parameter is ignored for multi-band rasters.",
action="store_false",
)
parser.add_argument(
Expand Down Expand Up @@ -134,7 +136,7 @@ def main(test_args: Sequence[str] = None) -> None:
dfact = 1

# Read image
img = Raster(args.filename, downsample=dfact)
img = Raster(args.filename, downsample=dfact, indexes=args.band)

# Set no data value
if args.nodata == "default":
Expand All @@ -157,7 +159,7 @@ def main(test_args: Sequence[str] = None) -> None:
perc, _ = args.vmin.split("%")
try:
perc = float(perc)
vmin = np.percentile(img.data, perc)
vmin = np.percentile(img.data.compressed(), perc)
except ValueError: # Case no % sign
raise ValueError("vmin must be a float or percentage, currently set to %s" % args.vmin)

Expand All @@ -172,7 +174,7 @@ def main(test_args: Sequence[str] = None) -> None:
perc, _ = args.vmax.split("%")
try:
perc = float(perc)
vmax = np.percentile(img.data, perc)
vmax = np.percentile(img.data.compressed(), perc)
except ValueError: # Case no % sign
raise ValueError("vmax must be a float or percentage, currently set to %s" % args.vmax)

Expand Down Expand Up @@ -213,7 +215,6 @@ def main(test_args: Sequence[str] = None) -> None:
# plot
img.show(
ax=ax,
index=args.band,
cmap=cmap,
interpolation="nearest",
vmin=vmin,
Expand Down
91 changes: 81 additions & 10 deletions tests/test_geoviewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
@pytest.mark.parametrize(
"option",
(
(),
("-cmap", "Reds"),
("-vmin", "-10", "-vmax", "10"),
("-vmin", "5%", "-vmax", "95%"),
("-band", "1"),
("-nocb",),
("-clabel", "Test"),
Expand All @@ -34,7 +36,7 @@
("-noresampl",),
),
) # type: ignore
def test_geoviewer_valid(capsys, monkeypatch, filename, option): # type: ignore
def test_geoviewer_valid_1band(capsys, monkeypatch, filename, option): # type: ignore
# To avoid having the plots popping up during execution
monkeypatch.setattr(plt, "show", lambda: None)

Expand All @@ -51,29 +53,98 @@ def test_geoviewer_valid(capsys, monkeypatch, filename, option): # type: ignore
assert output == ""

# Remove file if it was created
if option[0] == "-save":
if "-save" in option:
if os.path.exists("test.png"):
os.remove("test.png")


@pytest.mark.parametrize(
"filename", [gu.examples.get_path("everest_landsat_b4"), gu.examples.get_path("exploradores_aster_dem")]
) # type: ignore
@pytest.mark.parametrize(
"args",
(
(("-band", "0"), IndexError),
(("-band", "2"), IndexError),
(("-cmap", "Lols"), ValueError),
(("-cmap", "Lols"), ValueError),
(("-vmin", "lol"), ValueError),
(("-vmin", "lol2"), ValueError),
(("-vmax", "105%"), ValueError),
(("-figsize", "blabla"), ValueError),
(("-dpi", "300.5"), ValueError),
(("-nodata", "lol"), ValueError),
(("-nodata", "1e40"), ValueError),
),
) # type: ignore
def test_geoviewer_invalid_1band(capsys, monkeypatch, filename, args): # type: ignore
# To avoid having the plots popping up during execution
monkeypatch.setattr(plt, "show", lambda: None)

# To not get exception when testing generic functions such as --help
option, error = args
with pytest.raises(error):
geoviewer.main([filename, *option])


@pytest.mark.parametrize("filename", [gu.examples.get_path("everest_landsat_rgb")]) # type: ignore
@pytest.mark.parametrize(
"option",
(
("-cmap", "Lols"),
("-vmin", "lol"),
("-vmin", "lol2"),
("-figsize", "blabla"),
("-dpi", "300.5"),
("-nodata", "lol"),
(),
("-band", "1"),
("-band", "2"),
("-band", "3"),
("-clabel", "Test"),
("-figsize", "8,8"),
("-max_size", "1000"),
("-save", "test.png"),
("-dpi", "300"),
("-nodata", "99"),
("-noresampl",),
),
) # type: ignore
def test_geoviewer_valid_3band(capsys, monkeypatch, filename, option): # type: ignore
# To avoid having the plots popping up during execution
monkeypatch.setattr(plt, "show", lambda: None)

# To not get exception when testing generic functions such as --help
try:
geoviewer.main([filename, *option])
except SystemExit:
pass

# Capture error output (not stdout, just a plot)
output = capsys.readouterr().err

# No error should be raised
assert output == ""

# Remove file if it was created
if "-save" in option:
if os.path.exists("test.png"):
os.remove("test.png")


@pytest.mark.parametrize(
"filename",
[
gu.examples.get_path("everest_landsat_rgb"),
],
) # type: ignore
@pytest.mark.parametrize(
"args",
(
(("-band", "0"), IndexError),
(("-band", "4"), IndexError),
(("-nodata", "1e40"), ValueError),
),
) # type: ignore
def test_geoviewer_invalid(capsys, monkeypatch, filename, option): # type: ignore
def test_geoviewer_invalid_3band(capsys, monkeypatch, filename, args): # type: ignore
# To avoid having the plots popping up during execution
monkeypatch.setattr(plt, "show", lambda: None)

# To not get exception when testing generic functions such as --help
with pytest.raises(ValueError):
option, error = args
with pytest.raises(error):
geoviewer.main([filename, *option])