Skip to content

Commit

Permalink
Increase test coverage of gdal_footprint
Browse files Browse the repository at this point in the history
  • Loading branch information
rouault committed Nov 28, 2023
1 parent 35e4191 commit e7b8073
Show file tree
Hide file tree
Showing 4 changed files with 314 additions and 14 deletions.
1 change: 1 addition & 0 deletions autotest/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ add_executable(
test_osr_ct.cpp
test_osr_proj4.cpp
test_triangulation.cpp
test_utilities.cpp
test_marching_squares_contour.cpp
test_marching_squares_polygon.cpp
test_marching_squares_square.cpp
Expand Down
111 changes: 111 additions & 0 deletions autotest/cpp/test_utilities.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
///////////////////////////////////////////////////////////////////////////////
//
// Project: C++ Test Suite for GDAL/OGR
// Purpose: Test the C API of utilities as library functions
// Author: Even Rouault <even.rouault at spatialys.com>
//
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2023, Even Rouault <even.rouault at spatialys.com>
/*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
****************************************************************************/

#include "gdal_unit_test.h"

#include "cpl_error.h"
#include "cpl_string.h"
#include "gdal_priv.h"
#include "gdal_utils.h"

#include "gtest_include.h"

namespace
{

struct test_utilities : public ::testing::Test
{
};

TEST_F(test_utilities, GDALFootprint)
{
CPLErrorHandlerPusher oQuietErrors(CPLQuietErrorHandler);
// Test if (pszDest == nullptr && hDstDS == nullptr)
EXPECT_EQ(GDALFootprint(/* pszDest = */ nullptr,
/* hDstDS = */ nullptr,
/* hSrcDataset = */ nullptr,
/* psOptionsIn = */ nullptr,
/* pbUsageError = */ nullptr),
nullptr);

// Test if (hSrcDataset == nullptr)
EXPECT_EQ(GDALFootprint(/* pszDest = */ "/vsimem/out",
/* hDstDS = */ nullptr,
/* hSrcDataset = */ nullptr,
/* psOptionsIn = */ nullptr,
/* pbUsageError = */ nullptr),
nullptr);

// Test if (hDstDS != nullptr && psOptionsIn && psOptionsIn->bCreateOutput)
{
CPLStringList aosArgv;
aosArgv.AddString("-of");
aosArgv.AddString("Memory");
auto poMemDrv = GetGDALDriverManager()->GetDriverByName("Memory");
if (poMemDrv)
{
auto psOptions = GDALFootprintOptionsNew(aosArgv.List(), nullptr);
auto poInDS = std::unique_ptr<GDALDataset>(
poMemDrv->Create("", 0, 0, 0, GDT_Unknown, nullptr));
auto poOutDS = std::unique_ptr<GDALDataset>(
poMemDrv->Create("", 0, 0, 0, GDT_Unknown, nullptr));
EXPECT_EQ(
GDALFootprint(
/* pszDest = */ nullptr,
/* hDstDS = */ GDALDataset::ToHandle(poOutDS.get()),
/* hSrcDataset = */ GDALDataset::ToHandle(poInDS.get()),
/* psOptionsIn = */ psOptions,
/* pbUsageError = */ nullptr),
nullptr);
GDALFootprintOptionsFree(psOptions);
}
}

// Test if (psOptions == nullptr)
// and if (poSrcDS->GetRasterCount() == 0)
{
auto poMemDrv = GetGDALDriverManager()->GetDriverByName("Memory");
if (poMemDrv)
{
auto poInDS = std::unique_ptr<GDALDataset>(
poMemDrv->Create("", 0, 0, 0, GDT_Unknown, nullptr));
auto poOutDS = std::unique_ptr<GDALDataset>(
poMemDrv->Create("", 0, 0, 0, GDT_Unknown, nullptr));
EXPECT_EQ(
GDALFootprint(
/* pszDest = */ nullptr,
/* hDstDS = */ GDALDataset::ToHandle(poOutDS.get()),
/* hSrcDataset = */ GDALDataset::ToHandle(poInDS.get()),
/* psOptionsIn = */ nullptr,
/* pbUsageError = */ nullptr),
nullptr);
}
}
}

} // namespace
2 changes: 1 addition & 1 deletion autotest/utilities/test_gdal_footprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def test_gdal_footprint_basic(gdal_footprint_path, tmp_path):
footprint_json = str(tmp_path / "out_footprint.json")

(_, err) = gdaltest.runexternal_out_and_err(
gdal_footprint_path + f" -f GeoJSON ../gcore/data/byte.tif {footprint_json}"
gdal_footprint_path + f" -q -f GeoJSON ../gcore/data/byte.tif {footprint_json}"
)
assert err is None or err == "", "got error/warning"

Expand Down
214 changes: 201 additions & 13 deletions autotest/utilities/test_gdal_footprint_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
import ogrtest
import pytest

from osgeo import gdal
from osgeo import gdal, osr

pytestmark = pytest.mark.require_geos

Expand Down Expand Up @@ -398,7 +398,7 @@ def test_gdal_footprint_lib_dsco_lco(tmp_vsimem):
# Test option argument handling


def test_gdaldem_footprint_dict_arguments():
def test_gdal_footprint_footprint_dict_arguments():

opt = gdal.FootprintOptions(
"__RETURN_OPTION_LIST__",
Expand Down Expand Up @@ -433,7 +433,7 @@ def test_gdaldem_footprint_dict_arguments():
# Test footprint, RGBA and overviews


def test_gdaldem_footprint_rgba_overviews():
def test_gdal_footprint_footprint_rgba_overviews():

src_ds = gdal.GetDriverByName("MEM").Create("", 6, 6, 4)
for i in range(4):
Expand Down Expand Up @@ -516,21 +516,209 @@ def test_gdal_footprint_lib_intersection_partial():


###############################################################################
def test_gdal_footprint_lib_intersection_full():
def test_gdal_footprint_layerName():

src_ds = gdal.GetDriverByName("MEM").Create("", 3, 1, 2)
src_ds = gdal.GetDriverByName("MEM").Create("", 1, 1, 1)
src_ds.GetRasterBand(1).Fill(255)
out_ds = gdal.GetDriverByName("Memory").Create("", 0, 0, 0, gdal.GDT_Unknown)

with pytest.raises(Exception, match="Cannot find layer non_existing"):
gdal.Footprint(out_ds, src_ds, layerName="non_existing")

out_ds.CreateLayer("a")
layer_b = out_ds.CreateLayer("b")

gdal.Footprint(out_ds, src_ds, layerName="b")
assert layer_b.GetFeatureCount() == 1


###############################################################################
def test_gdal_footprint_wrong_output_format():

src_ds = gdal.GetDriverByName("MEM").Create("", 1, 1, 1)

# Non existing output driver
with pytest.raises(Exception, match="Output driver `non_existing' not recognised"):
gdal.Footprint("", src_ds, format="non_existing")

# Raster-only output driver
with pytest.raises(Exception, match="Output driver `GTiff' not recognised"):
gdal.Footprint("", src_ds, format="GTiff")

with pytest.raises(
Exception, match="Cannot guess driver for /vsimem/out.unknown_ext"
):
gdal.Footprint(
"/vsimem/out.unknown_ext",
src_ds,
)


###############################################################################
def test_gdal_footprint_output_layer_has_crs_but_input_not():

src_ds = gdal.GetDriverByName("MEM").Create("", 1, 1, 1)
out_ds = gdal.GetDriverByName("Memory").Create("", 0, 0, 0, gdal.GDT_Unknown)
srs = osr.SpatialReference()
srs.ImportFromEPSG(4326)
out_ds.CreateLayer("out_lyr", srs=srs)

with pytest.raises(
Exception, match="Output layer has CRS, but input is not georeferenced"
):
gdal.Footprint(out_ds, src_ds, layerName="out_lyr")


###############################################################################
def test_gdal_footprint_wrong_number_nodata_values():

src_ds = gdal.GetDriverByName("MEM").Create("", 1, 1, 1)
with pytest.raises(
Exception,
match="Number of values in -srcnodata should be 1 or the number of bands",
):
gdal.Footprint("", src_ds, format="Memory", srcNodata=[1, 2])


###############################################################################
def test_gdal_footprint_wrong_bands():

src_ds = gdal.GetDriverByName("MEM").Create("", 1, 1, 1)
with pytest.raises(Exception, match="Invalid band number: 2"):
gdal.Footprint("", src_ds, format="Memory", bands=[2])


###############################################################################
def test_gdal_footprint_wrong_ovr_on_band_with_nodata():

src_ds = gdal.GetDriverByName("MEM").Create("", 2, 2, 1)
src_ds.GetRasterBand(1).SetNoDataValue(0)
src_ds.GetRasterBand(1).WriteRaster(0, 0, 2, 1, b"\xFF\xFF")
src_ds.GetRasterBand(2).SetNoDataValue(0)
src_ds.GetRasterBand(2).WriteRaster(0, 0, 2, 1, b"\xFF\xFF")
with pytest.raises(
Exception,
match="Overview index 0 invalid for this dataset. Bands of this dataset have no precomputed overviews",
):
gdal.Footprint(
"",
src_ds,
format="Memory",
ovr=0,
)
src_ds.BuildOverviews("NEAR", [2])
with pytest.raises(
Exception,
match=r"Overview index 1 invalid for this dataset. Value should be in \[0,0\] range",
):
gdal.Footprint(
"",
src_ds,
format="Memory",
ovr=1,
)


###############################################################################
def test_gdal_footprint_wrong_ovr_on_band_with_alpha():

src_ds = gdal.GetDriverByName("MEM").Create("", 2, 2, 2)
src_ds.GetRasterBand(2).SetColorInterpretation(gdal.GCI_AlphaBand)
with pytest.raises(
Exception,
match="Overview index 0 invalid for this dataset. Mask bands of this dataset have no precomputed overviews",
):
gdal.Footprint(
"",
src_ds,
format="Memory",
ovr=0,
)
src_ds.BuildOverviews("NEAR", [2])
with pytest.raises(
Exception,
match=r"Overview index 1 invalid for this dataset. Value should be in \[0,0\] range",
):
gdal.Footprint(
"",
src_ds,
format="Memory",
ovr=1,
)


###############################################################################
#


def test_gdal_footprint_lib_targetCoordinateSystem_georef_error():

src_ds = gdal.GetDriverByName("MEM").Create("", 1, 1, 1)
with pytest.raises(
Exception,
match="Georeferenced coordinates requested, but input dataset has no geotransform.",
):
gdal.Footprint(
"",
src_ds,
format="Memory",
targetCoordinateSystem="georef",
)


###############################################################################
#


def test_gdal_footprint_lib_minRingArea():

# footprint area above minRingArea
src_ds = gdal.GetDriverByName("MEM").Create("", 1, 1, 1)
out_ds = gdal.Footprint(
"",
src_ds,
format="Memory",
targetCoordinateSystem="pixel",
combineBands="intersection",
minRingArea="0.5",
)
assert out_ds is not None
lyr = out_ds.GetLayer(0)
f = lyr.GetNextFeature()
ogrtest.check_feature_geometry(f, "MULTIPOLYGON (((0 0,0 1,2 1,2 0,0 0)))")
out_lyr = out_ds.GetLayer(0)
assert out_lyr.GetFeatureCount() == 1

# footprint area below minRingArea
src_ds = gdal.GetDriverByName("MEM").Create("", 1, 1, 1)
out_ds = gdal.Footprint(
"",
src_ds,
format="Memory",
targetCoordinateSystem="pixel",
minRingArea="1.5",
)
out_lyr = out_ds.GetLayer(0)
assert out_lyr.GetFeatureCount() == 0


###############################################################################
#


def test_gdal_footprint_lib_destSRS_and_targetCoordinateSystem_pixel_mutually_exclusive():

with pytest.raises(
Exception, match="-t_cs pixel and -t_srs are mutually exclusive"
):
gdal.Footprint(
"",
"../gcore/data/byte.tif",
format="Memory",
dstSRS="EPSG:4267",
targetCoordinateSystem="pixel",
)


###############################################################################
#


def test_gdal_footprint_lib_srcNodata_and_ovr_mutually_exclusive():

with pytest.raises(Exception, match="-srcnodata and -ovr are mutually exclusive"):
gdal.Footprint(
"", "../gcore/data/byte.tif", format="Memory", srcNodata=0, ovr=0
)

0 comments on commit e7b8073

Please sign in to comment.