mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-04 00:04:03 -04:00
355 lines
15 KiB
C++
355 lines
15 KiB
C++
/***************************************************************************
|
|
testqgsgdalutils.cpp
|
|
--------------------
|
|
begin : September 2018
|
|
copyright : (C) 2018 Even Rouault
|
|
email : even.rouault at spatialys.com
|
|
***************************************************************************
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
***************************************************************************/
|
|
#include "qgstest.h"
|
|
#include <QObject>
|
|
#include <QString>
|
|
#include <QStringList>
|
|
#include <QSettings>
|
|
|
|
#include <gdal.h>
|
|
|
|
#include "qgsgdalutils.h"
|
|
#include "qgsapplication.h"
|
|
#include "qgsrasterlayer.h"
|
|
|
|
|
|
class TestQgsGdalUtils : public QObject
|
|
{
|
|
Q_OBJECT
|
|
|
|
private slots:
|
|
void initTestCase(); // will be called before the first testfunction is executed.
|
|
void cleanupTestCase(); // will be called after the last testfunction was executed.
|
|
void init(); // will be called before each testfunction is executed.
|
|
void cleanup(); // will be called after every testfunction.
|
|
void supportsRasterCreate();
|
|
void testCreateSingleBandMemoryDataset();
|
|
void testCreateMultiBandMemoryDataset();
|
|
void testCreateSingleBandTiffDataset();
|
|
void testResampleSingleBandRaster();
|
|
void testImageToDataset();
|
|
void testResampleImageToImage();
|
|
void testPathIsCheapToOpen();
|
|
void testVrtMatchesLayerType();
|
|
void testMultilayerExtensions();
|
|
void testResampleSingleBandRasterNoData();
|
|
|
|
private:
|
|
double identify( GDALDatasetH dataset, int band, int px, int py );
|
|
};
|
|
|
|
void TestQgsGdalUtils::initTestCase()
|
|
{
|
|
QgsApplication::init();
|
|
QgsApplication::initQgis();
|
|
GDALAllRegister();
|
|
}
|
|
|
|
void TestQgsGdalUtils::cleanupTestCase()
|
|
{
|
|
QgsApplication::exitQgis();
|
|
}
|
|
|
|
void TestQgsGdalUtils::init()
|
|
{
|
|
}
|
|
|
|
void TestQgsGdalUtils::cleanup()
|
|
{
|
|
}
|
|
|
|
void TestQgsGdalUtils::supportsRasterCreate()
|
|
{
|
|
QVERIFY( QgsGdalUtils::supportsRasterCreate( GDALGetDriverByName( "GTiff" ) ) );
|
|
QVERIFY( QgsGdalUtils::supportsRasterCreate( GDALGetDriverByName( "GPKG" ) ) );
|
|
|
|
// special case
|
|
QVERIFY( !QgsGdalUtils::supportsRasterCreate( GDALGetDriverByName( "SQLite" ) ) );
|
|
|
|
// create-only
|
|
QVERIFY( !QgsGdalUtils::supportsRasterCreate( GDALGetDriverByName( "DTED" ) ) );
|
|
|
|
// vector-only
|
|
QVERIFY( !QgsGdalUtils::supportsRasterCreate( GDALGetDriverByName( "ESRI Shapefile" ) ) );
|
|
}
|
|
|
|
#define EPSG_4326_WKT \
|
|
"GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]]," \
|
|
"AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433," \
|
|
"AUTHORITY[\"EPSG\",\"9122\"]],AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY[\"EPSG\",\"4326\"]]"
|
|
|
|
void TestQgsGdalUtils::testCreateSingleBandMemoryDataset()
|
|
{
|
|
const gdal::dataset_unique_ptr ds1 = QgsGdalUtils::createSingleBandMemoryDataset( GDT_Float32, QgsRectangle( 1, 1, 21, 11 ), 40, 20, QgsCoordinateReferenceSystem( "EPSG:4326" ) );
|
|
QVERIFY( ds1 );
|
|
|
|
QCOMPARE( GDALGetRasterCount( ds1.get() ), 1 );
|
|
QCOMPARE( GDALGetRasterXSize( ds1.get() ), 40 );
|
|
QCOMPARE( GDALGetRasterYSize( ds1.get() ), 20 );
|
|
|
|
QCOMPARE( GDALGetProjectionRef( ds1.get() ), R"""(GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AXIS["Latitude",NORTH],AXIS["Longitude",EAST],AUTHORITY["EPSG","4326"]])""" );
|
|
double geoTransform[6];
|
|
double geoTransformExpected[] = { 1, 0.5, 0, 11, 0, -0.5 };
|
|
QCOMPARE( GDALGetGeoTransform( ds1.get(), geoTransform ), CE_None );
|
|
QVERIFY( memcmp( geoTransform, geoTransformExpected, sizeof( double ) * 6 ) == 0 );
|
|
|
|
QCOMPARE( GDALGetRasterDataType( GDALGetRasterBand( ds1.get(), 1 ) ), GDT_Float32 );
|
|
}
|
|
|
|
void TestQgsGdalUtils::testCreateMultiBandMemoryDataset()
|
|
{
|
|
const gdal::dataset_unique_ptr ds1 = QgsGdalUtils::createMultiBandMemoryDataset( GDT_Float32, 4, QgsRectangle( 1, 1, 21, 11 ), 40, 20, QgsCoordinateReferenceSystem( "EPSG:4326" ) );
|
|
QVERIFY( ds1 );
|
|
|
|
QCOMPARE( GDALGetRasterCount( ds1.get() ), 4 );
|
|
QCOMPARE( GDALGetRasterXSize( ds1.get() ), 40 );
|
|
QCOMPARE( GDALGetRasterYSize( ds1.get() ), 20 );
|
|
|
|
QCOMPARE( GDALGetProjectionRef( ds1.get() ), R"""(GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AXIS["Latitude",NORTH],AXIS["Longitude",EAST],AUTHORITY["EPSG","4326"]])""" );
|
|
double geoTransform[6];
|
|
double geoTransformExpected[] = { 1, 0.5, 0, 11, 0, -0.5 };
|
|
QCOMPARE( GDALGetGeoTransform( ds1.get(), geoTransform ), CE_None );
|
|
QVERIFY( memcmp( geoTransform, geoTransformExpected, sizeof( double ) * 6 ) == 0 );
|
|
|
|
QCOMPARE( GDALGetRasterDataType( GDALGetRasterBand( ds1.get(), 1 ) ), GDT_Float32 );
|
|
QCOMPARE( GDALGetRasterDataType( GDALGetRasterBand( ds1.get(), 2 ) ), GDT_Float32 );
|
|
QCOMPARE( GDALGetRasterDataType( GDALGetRasterBand( ds1.get(), 3 ) ), GDT_Float32 );
|
|
QCOMPARE( GDALGetRasterDataType( GDALGetRasterBand( ds1.get(), 4 ) ), GDT_Float32 );
|
|
}
|
|
|
|
void TestQgsGdalUtils::testCreateSingleBandTiffDataset()
|
|
{
|
|
const QString filename = QDir::tempPath() + "/qgis_test_single_band_raster.tif";
|
|
QFile::remove( filename );
|
|
QVERIFY( !QFile::exists( filename ) );
|
|
|
|
gdal::dataset_unique_ptr ds1 = QgsGdalUtils::createSingleBandTiffDataset( filename, GDT_Float32, QgsRectangle( 1, 1, 21, 11 ), 40, 20, QgsCoordinateReferenceSystem( "EPSG:4326" ) );
|
|
QVERIFY( ds1 );
|
|
|
|
QCOMPARE( GDALGetRasterCount( ds1.get() ), 1 );
|
|
QCOMPARE( GDALGetRasterXSize( ds1.get() ), 40 );
|
|
QCOMPARE( GDALGetRasterYSize( ds1.get() ), 20 );
|
|
|
|
QCOMPARE( GDALGetProjectionRef( ds1.get() ), R"""(GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AXIS["Latitude",NORTH],AXIS["Longitude",EAST],AUTHORITY["EPSG","4326"]])""" );
|
|
|
|
double geoTransform[6];
|
|
double geoTransformExpected[] = { 1, 0.5, 0, 11, 0, -0.5 };
|
|
QCOMPARE( GDALGetGeoTransform( ds1.get(), geoTransform ), CE_None );
|
|
QVERIFY( memcmp( geoTransform, geoTransformExpected, sizeof( double ) * 6 ) == 0 );
|
|
|
|
QCOMPARE( GDALGetRasterDataType( GDALGetRasterBand( ds1.get(), 1 ) ), GDT_Float32 );
|
|
|
|
ds1.reset(); // makes sure the file is fully written
|
|
|
|
QVERIFY( QFile::exists( filename ) );
|
|
|
|
auto layer = std::make_unique<QgsRasterLayer>( filename, "test", "gdal" );
|
|
QVERIFY( layer->isValid() );
|
|
QCOMPARE( layer->extent(), QgsRectangle( 1, 1, 21, 11 ) );
|
|
QCOMPARE( layer->width(), 40 );
|
|
QCOMPARE( layer->height(), 20 );
|
|
|
|
layer.reset(); // let's clean up before removing the file
|
|
QFile::remove( filename );
|
|
}
|
|
|
|
void TestQgsGdalUtils::testResampleSingleBandRaster()
|
|
{
|
|
const QString inputFilename = QString( TEST_DATA_DIR ) + "/float1-16.tif";
|
|
const gdal::dataset_unique_ptr srcDS( GDALOpen( inputFilename.toUtf8().constData(), GA_ReadOnly ) );
|
|
QVERIFY( srcDS );
|
|
|
|
const QString outputFilename = QDir::tempPath() + "/qgis_test_float1-16_resampled.tif";
|
|
const QgsRectangle outputExtent( 106.25, -6.75, 106.55, -6.45 );
|
|
gdal::dataset_unique_ptr dstDS = QgsGdalUtils::createSingleBandTiffDataset( outputFilename, GDT_Float32, outputExtent, 2, 2, QgsCoordinateReferenceSystem( "EPSG:4326" ) );
|
|
QVERIFY( dstDS );
|
|
|
|
QgsGdalUtils::resampleSingleBandRaster( srcDS.get(), dstDS.get(), GRA_NearestNeighbour, nullptr );
|
|
dstDS.reset();
|
|
|
|
auto layer = std::make_unique<QgsRasterLayer>( outputFilename, "test", "gdal" );
|
|
QVERIFY( layer );
|
|
std::unique_ptr<QgsRasterBlock> block( layer->dataProvider()->block( 1, outputExtent, 2, 2 ) );
|
|
QVERIFY( block );
|
|
QCOMPARE( block->value( 0, 0 ), 6. );
|
|
QCOMPARE( block->value( 1, 1 ), 11. );
|
|
|
|
layer.reset();
|
|
QFile::remove( outputFilename );
|
|
}
|
|
|
|
void TestQgsGdalUtils::testImageToDataset()
|
|
{
|
|
const QString inputFilename = QString( TEST_DATA_DIR ) + "/rgb256x256.png";
|
|
QImage src = QImage( inputFilename );
|
|
src = src.convertToFormat( QImage::Format_ARGB32 );
|
|
QVERIFY( !src.isNull() );
|
|
|
|
gdal::dataset_unique_ptr dstDS = QgsGdalUtils::imageToMemoryDataset( QImage() );
|
|
QVERIFY( !dstDS );
|
|
|
|
dstDS = QgsGdalUtils::imageToMemoryDataset( src );
|
|
QVERIFY( dstDS );
|
|
|
|
QCOMPARE( GDALGetRasterCount( dstDS.get() ), 4 );
|
|
QCOMPARE( GDALGetRasterXSize( dstDS.get() ), 256 );
|
|
QCOMPARE( GDALGetRasterYSize( dstDS.get() ), 256 );
|
|
|
|
QCOMPARE( GDALGetRasterDataType( GDALGetRasterBand( dstDS.get(), 1 ) ), GDT_Byte );
|
|
QCOMPARE( GDALGetRasterDataType( GDALGetRasterBand( dstDS.get(), 2 ) ), GDT_Byte );
|
|
QCOMPARE( GDALGetRasterDataType( GDALGetRasterBand( dstDS.get(), 3 ) ), GDT_Byte );
|
|
QCOMPARE( GDALGetRasterDataType( GDALGetRasterBand( dstDS.get(), 4 ) ), GDT_Byte );
|
|
|
|
QCOMPARE( identify( dstDS.get(), 1, 50, 50 ), 255.0 );
|
|
QCOMPARE( identify( dstDS.get(), 1, 200, 50 ), 255.0 );
|
|
QCOMPARE( identify( dstDS.get(), 1, 50, 200 ), 0.0 );
|
|
QCOMPARE( identify( dstDS.get(), 1, 200, 200 ), 0.0 );
|
|
|
|
QCOMPARE( identify( dstDS.get(), 2, 50, 50 ), 255.0 );
|
|
QCOMPARE( identify( dstDS.get(), 2, 200, 50 ), 0.0 );
|
|
QCOMPARE( identify( dstDS.get(), 2, 50, 200 ), 255.0 );
|
|
QCOMPARE( identify( dstDS.get(), 2, 200, 200 ), 0.0 );
|
|
|
|
QCOMPARE( identify( dstDS.get(), 3, 50, 50 ), 0.0 );
|
|
QCOMPARE( identify( dstDS.get(), 3, 200, 50 ), 0.0 );
|
|
QCOMPARE( identify( dstDS.get(), 3, 50, 200 ), 0.0 );
|
|
QCOMPARE( identify( dstDS.get(), 3, 200, 200 ), 255.0 );
|
|
|
|
QCOMPARE( identify( dstDS.get(), 4, 50, 50 ), 255.0 );
|
|
QCOMPARE( identify( dstDS.get(), 4, 200, 50 ), 255.0 );
|
|
QCOMPARE( identify( dstDS.get(), 4, 50, 200 ), 255.0 );
|
|
QCOMPARE( identify( dstDS.get(), 4, 200, 200 ), 255.0 );
|
|
}
|
|
|
|
void TestQgsGdalUtils::testResampleImageToImage()
|
|
{
|
|
const QString inputFilename = QString( TEST_DATA_DIR ) + "/rgb256x256.png";
|
|
QImage src = QImage( inputFilename );
|
|
src = src.convertToFormat( QImage::Format_ARGB32 );
|
|
QVERIFY( !src.isNull() );
|
|
|
|
QImage res = QgsGdalUtils::resampleImage( QImage(), QSize( 50, 50 ), GRIORA_NearestNeighbour );
|
|
QVERIFY( res.isNull() );
|
|
|
|
res = QgsGdalUtils::resampleImage( src, QSize( 50, 50 ), GRIORA_NearestNeighbour );
|
|
QVERIFY( !res.isNull() );
|
|
QCOMPARE( res.width(), 50 );
|
|
QCOMPARE( res.height(), 50 );
|
|
|
|
QCOMPARE( qRed( res.pixel( 10, 10 ) ), 255 );
|
|
QCOMPARE( qGreen( res.pixel( 10, 10 ) ), 255 );
|
|
QCOMPARE( qBlue( res.pixel( 10, 10 ) ), 0 );
|
|
QCOMPARE( qAlpha( res.pixel( 10, 10 ) ), 255 );
|
|
|
|
QCOMPARE( qRed( res.pixel( 40, 10 ) ), 255 );
|
|
QCOMPARE( qGreen( res.pixel( 40, 10 ) ), 0 );
|
|
QCOMPARE( qBlue( res.pixel( 40, 10 ) ), 0 );
|
|
QCOMPARE( qAlpha( res.pixel( 40, 10 ) ), 255 );
|
|
|
|
QCOMPARE( qRed( res.pixel( 10, 40 ) ), 0 );
|
|
QCOMPARE( qGreen( res.pixel( 10, 40 ) ), 255 );
|
|
QCOMPARE( qBlue( res.pixel( 10, 40 ) ), 0 );
|
|
QCOMPARE( qAlpha( res.pixel( 10, 40 ) ), 255 );
|
|
|
|
QCOMPARE( qRed( res.pixel( 40, 40 ) ), 0 );
|
|
QCOMPARE( qGreen( res.pixel( 40, 40 ) ), 0 );
|
|
QCOMPARE( qBlue( res.pixel( 40, 40 ) ), 255 );
|
|
QCOMPARE( qAlpha( res.pixel( 40, 40 ) ), 255 );
|
|
}
|
|
|
|
void TestQgsGdalUtils::testPathIsCheapToOpen()
|
|
{
|
|
// should be safe and report false paths which don't exist
|
|
QVERIFY( !QgsGdalUtils::pathIsCheapToOpen( "/not/a/file" ) );
|
|
|
|
// for now, tiff aren't marked as cheap to open
|
|
QVERIFY( !QgsGdalUtils::pathIsCheapToOpen( QStringLiteral( TEST_DATA_DIR ) + "/big_raster.tif" ) );
|
|
|
|
// a csv is considered cheap to open
|
|
QVERIFY( QgsGdalUtils::pathIsCheapToOpen( QStringLiteral( TEST_DATA_DIR ) + "/delimitedtext/test.csv" ) );
|
|
|
|
// ... unless it's larger than the specified file size limit (500 bytes)
|
|
QVERIFY( !QgsGdalUtils::pathIsCheapToOpen( QStringLiteral( TEST_DATA_DIR ) + "/delimitedtext/testdms.csv", 500 ) );
|
|
}
|
|
|
|
void TestQgsGdalUtils::testVrtMatchesLayerType()
|
|
{
|
|
QVERIFY( QgsGdalUtils::vrtMatchesLayerType( QStringLiteral( TEST_DATA_DIR ) + "/raster/hub13263.vrt", Qgis::LayerType::Raster ) );
|
|
QVERIFY( !QgsGdalUtils::vrtMatchesLayerType( QStringLiteral( TEST_DATA_DIR ) + "/raster/hub13263.vrt", Qgis::LayerType::Vector ) );
|
|
|
|
QVERIFY( !QgsGdalUtils::vrtMatchesLayerType( QStringLiteral( TEST_DATA_DIR ) + "/vector_vrt.vrt", Qgis::LayerType::Raster ) );
|
|
QVERIFY( QgsGdalUtils::vrtMatchesLayerType( QStringLiteral( TEST_DATA_DIR ) + "/vector_vrt.vrt", Qgis::LayerType::Vector ) );
|
|
}
|
|
|
|
void TestQgsGdalUtils::testMultilayerExtensions()
|
|
{
|
|
const QStringList extensions = QgsGdalUtils::multiLayerFileExtensions();
|
|
QVERIFY( extensions.contains( QStringLiteral( "gpkg" ) ) );
|
|
QVERIFY( extensions.contains( QStringLiteral( "sqlite" ) ) );
|
|
QVERIFY( extensions.contains( QStringLiteral( "db" ) ) );
|
|
QVERIFY( extensions.contains( QStringLiteral( "kml" ) ) );
|
|
QVERIFY( extensions.contains( QStringLiteral( "ods" ) ) );
|
|
QVERIFY( extensions.contains( QStringLiteral( "osm" ) ) );
|
|
QVERIFY( extensions.contains( QStringLiteral( "mdb" ) ) );
|
|
QVERIFY( extensions.contains( QStringLiteral( "xls" ) ) );
|
|
QVERIFY( extensions.contains( QStringLiteral( "xlsx" ) ) );
|
|
QVERIFY( extensions.contains( QStringLiteral( "gpx" ) ) );
|
|
QVERIFY( extensions.contains( QStringLiteral( "pdf" ) ) );
|
|
QVERIFY( extensions.contains( QStringLiteral( "nc" ) ) );
|
|
QVERIFY( extensions.contains( QStringLiteral( "gdb" ) ) );
|
|
}
|
|
|
|
double TestQgsGdalUtils::identify( GDALDatasetH dataset, int band, int px, int py )
|
|
{
|
|
GDALRasterBandH hBand = GDALGetRasterBand( dataset, band );
|
|
|
|
float *pafScanline = ( float * ) CPLMalloc( sizeof( float ) );
|
|
const CPLErr err = GDALRasterIO( hBand, GF_Read, px, py, 1, 1, pafScanline, 1, 1, GDT_Float32, 0, 0 );
|
|
const double value = err == CE_None ? pafScanline[0] : std::numeric_limits<double>::quiet_NaN();
|
|
CPLFree( pafScanline );
|
|
|
|
return value;
|
|
}
|
|
|
|
void TestQgsGdalUtils::testResampleSingleBandRasterNoData()
|
|
{
|
|
// Create 2x2 integer raster with values 1, 65535, 65535, 65535 and nodata=65535
|
|
QTemporaryDir tempDir;
|
|
const QString tempPath = tempDir.path();
|
|
const QString inputFilename = tempPath + "/input.tif";
|
|
|
|
gdal::dataset_unique_ptr ds1 = QgsGdalUtils::createSingleBandTiffDataset( inputFilename, GDT_UInt16, QgsRectangle( 1, 1, 3, 3 ), 2, 2, QgsCoordinateReferenceSystem( "EPSG:4326" ) );
|
|
QVERIFY( ds1 );
|
|
|
|
GDALRasterBandH band = GDALGetRasterBand( ds1.get(), 1 );
|
|
GDALSetRasterNoDataValue( band, 65535 );
|
|
GUInt16 buffer[4] = { 1, 65535, 65535, 65535 };
|
|
const CPLErr err = GDALRasterIO( band, GF_Write, 0, 0, 2, 2, buffer, 2, 2, GDT_UInt16, 0, 0 );
|
|
QCOMPARE( err, CE_None );
|
|
|
|
// Create destination GDAL DS
|
|
auto dstBlock = std::make_unique<QgsRasterBlock>( Qgis::DataType::UInt16, 4, 4 );
|
|
dstBlock->setNoDataValue( 65535.0 );
|
|
gdal::dataset_unique_ptr gdalDsOutput = QgsGdalUtils::blockToSingleBandMemoryDataset( QgsRectangle( 1, 1, 3, 3 ), dstBlock.get() );
|
|
|
|
QVERIFY( QgsGdalUtils::resampleSingleBandRaster( ds1.get(), gdalDsOutput.get(), GRA_NearestNeighbour, nullptr ) );
|
|
QCOMPARE( identify( gdalDsOutput.get(), 1, 0, 0 ), 1.0 );
|
|
QCOMPARE( identify( gdalDsOutput.get(), 1, 2, 2 ), 65535.0 );
|
|
}
|
|
|
|
|
|
QGSTEST_MAIN( TestQgsGdalUtils )
|
|
#include "testqgsgdalutils.moc"
|