1 /***************************************************************************
2 testqgsgdalutils.cpp
3 --------------------
4 begin : September 2018
5 copyright : (C) 2018 Even Rouault
6 email : even.rouault at spatialys.com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15 #include "qgstest.h"
16 #include <QObject>
17 #include <QString>
18 #include <QStringList>
19 #include <QSettings>
20
21 #include <gdal.h>
22
23 #include "qgsgdalutils.h"
24 #include "qgsapplication.h"
25 #include "qgsrasterlayer.h"
26
27
28 class TestQgsGdalUtils: public QObject
29 {
30 Q_OBJECT
31
32 private slots:
33 void initTestCase();// will be called before the first testfunction is executed.
34 void cleanupTestCase();// will be called after the last testfunction was executed.
35 void init();// will be called before each testfunction is executed.
36 void cleanup();// will be called after every testfunction.
37 void supportsRasterCreate();
38 void testCreateSingleBandMemoryDataset();
39 void testCreateMultiBandMemoryDataset();
40 void testCreateSingleBandTiffDataset();
41 void testResampleSingleBandRaster();
42 void testImageToDataset();
43 void testResampleImageToImage();
44 void testPathIsCheapToOpen();
45 void testVrtMatchesLayerType();
46 void testMultilayerExtensions();
47
48 private:
49
50 double identify( GDALDatasetH dataset, int band, int px, int py );
51 };
52
initTestCase()53 void TestQgsGdalUtils::initTestCase()
54 {
55 QgsApplication::init();
56 QgsApplication::initQgis();
57 GDALAllRegister();
58 }
59
cleanupTestCase()60 void TestQgsGdalUtils::cleanupTestCase()
61 {
62 QgsApplication::exitQgis();
63 }
64
init()65 void TestQgsGdalUtils::init()
66 {
67
68 }
69
cleanup()70 void TestQgsGdalUtils::cleanup()
71 {
72
73 }
74
supportsRasterCreate()75 void TestQgsGdalUtils::supportsRasterCreate()
76 {
77 QVERIFY( QgsGdalUtils::supportsRasterCreate( GDALGetDriverByName( "GTiff" ) ) );
78 QVERIFY( QgsGdalUtils::supportsRasterCreate( GDALGetDriverByName( "GPKG" ) ) );
79
80 // special case
81 QVERIFY( !QgsGdalUtils::supportsRasterCreate( GDALGetDriverByName( "SQLite" ) ) );
82
83 // create-only
84 QVERIFY( !QgsGdalUtils::supportsRasterCreate( GDALGetDriverByName( "DTED" ) ) );
85
86 // vector-only
87 QVERIFY( !QgsGdalUtils::supportsRasterCreate( GDALGetDriverByName( "ESRI Shapefile" ) ) );
88 }
89
90 #define EPSG_4326_WKT \
91 "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]]," \
92 "AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433," \
93 "AUTHORITY[\"EPSG\",\"9122\"]],AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY[\"EPSG\",\"4326\"]]"
94
testCreateSingleBandMemoryDataset()95 void TestQgsGdalUtils::testCreateSingleBandMemoryDataset()
96 {
97 const gdal::dataset_unique_ptr ds1 = QgsGdalUtils::createSingleBandMemoryDataset( GDT_Float32, QgsRectangle( 1, 1, 21, 11 ), 40, 20, QgsCoordinateReferenceSystem( "EPSG:4326" ) );
98 QVERIFY( ds1 );
99
100 QCOMPARE( GDALGetRasterCount( ds1.get() ), 1 );
101 QCOMPARE( GDALGetRasterXSize( ds1.get() ), 40 );
102 QCOMPARE( GDALGetRasterYSize( ds1.get() ), 20 );
103
104 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"]])""" );
105 double geoTransform[6];
106 double geoTransformExpected[] = { 1, 0.5, 0, 11, 0, -0.5 };
107 QCOMPARE( GDALGetGeoTransform( ds1.get(), geoTransform ), CE_None );
108 QVERIFY( memcmp( geoTransform, geoTransformExpected, sizeof( double ) * 6 ) == 0 );
109
110 QCOMPARE( GDALGetRasterDataType( GDALGetRasterBand( ds1.get(), 1 ) ), GDT_Float32 );
111 }
112
testCreateMultiBandMemoryDataset()113 void TestQgsGdalUtils::testCreateMultiBandMemoryDataset()
114 {
115 const gdal::dataset_unique_ptr ds1 = QgsGdalUtils::createMultiBandMemoryDataset( GDT_Float32, 4, QgsRectangle( 1, 1, 21, 11 ), 40, 20, QgsCoordinateReferenceSystem( "EPSG:4326" ) );
116 QVERIFY( ds1 );
117
118 QCOMPARE( GDALGetRasterCount( ds1.get() ), 4 );
119 QCOMPARE( GDALGetRasterXSize( ds1.get() ), 40 );
120 QCOMPARE( GDALGetRasterYSize( ds1.get() ), 20 );
121
122 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"]])""" );
123 double geoTransform[6];
124 double geoTransformExpected[] = { 1, 0.5, 0, 11, 0, -0.5 };
125 QCOMPARE( GDALGetGeoTransform( ds1.get(), geoTransform ), CE_None );
126 QVERIFY( memcmp( geoTransform, geoTransformExpected, sizeof( double ) * 6 ) == 0 );
127
128 QCOMPARE( GDALGetRasterDataType( GDALGetRasterBand( ds1.get(), 1 ) ), GDT_Float32 );
129 QCOMPARE( GDALGetRasterDataType( GDALGetRasterBand( ds1.get(), 2 ) ), GDT_Float32 );
130 QCOMPARE( GDALGetRasterDataType( GDALGetRasterBand( ds1.get(), 3 ) ), GDT_Float32 );
131 QCOMPARE( GDALGetRasterDataType( GDALGetRasterBand( ds1.get(), 4 ) ), GDT_Float32 );
132 }
133
testCreateSingleBandTiffDataset()134 void TestQgsGdalUtils::testCreateSingleBandTiffDataset()
135 {
136 const QString filename = QDir::tempPath() + "/qgis_test_single_band_raster.tif";
137 QFile::remove( filename );
138 QVERIFY( !QFile::exists( filename ) );
139
140 gdal::dataset_unique_ptr ds1 = QgsGdalUtils::createSingleBandTiffDataset( filename, GDT_Float32, QgsRectangle( 1, 1, 21, 11 ), 40, 20, QgsCoordinateReferenceSystem( "EPSG:4326" ) );
141 QVERIFY( ds1 );
142
143 QCOMPARE( GDALGetRasterCount( ds1.get() ), 1 );
144 QCOMPARE( GDALGetRasterXSize( ds1.get() ), 40 );
145 QCOMPARE( GDALGetRasterYSize( ds1.get() ), 20 );
146
147 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"]])""" );
148
149 double geoTransform[6];
150 double geoTransformExpected[] = { 1, 0.5, 0, 11, 0, -0.5 };
151 QCOMPARE( GDALGetGeoTransform( ds1.get(), geoTransform ), CE_None );
152 QVERIFY( memcmp( geoTransform, geoTransformExpected, sizeof( double ) * 6 ) == 0 );
153
154 QCOMPARE( GDALGetRasterDataType( GDALGetRasterBand( ds1.get(), 1 ) ), GDT_Float32 );
155
156 ds1.reset(); // makes sure the file is fully written
157
158 QVERIFY( QFile::exists( filename ) );
159
160 std::unique_ptr<QgsRasterLayer> layer( new QgsRasterLayer( filename, "test", "gdal" ) );
161 QVERIFY( layer->isValid() );
162 QCOMPARE( layer->extent(), QgsRectangle( 1, 1, 21, 11 ) );
163 QCOMPARE( layer->width(), 40 );
164 QCOMPARE( layer->height(), 20 );
165
166 layer.reset(); // let's clean up before removing the file
167 QFile::remove( filename );
168 }
169
testResampleSingleBandRaster()170 void TestQgsGdalUtils::testResampleSingleBandRaster()
171 {
172 const QString inputFilename = QString( TEST_DATA_DIR ) + "/float1-16.tif";
173 const gdal::dataset_unique_ptr srcDS( GDALOpen( inputFilename.toUtf8().constData(), GA_ReadOnly ) );
174 QVERIFY( srcDS );
175
176 const QString outputFilename = QDir::tempPath() + "/qgis_test_float1-16_resampled.tif";
177 const QgsRectangle outputExtent( 106.25, -6.75, 106.55, -6.45 );
178 gdal::dataset_unique_ptr dstDS = QgsGdalUtils::createSingleBandTiffDataset( outputFilename, GDT_Float32, outputExtent, 2, 2, QgsCoordinateReferenceSystem( "EPSG:4326" ) );
179 QVERIFY( dstDS );
180
181 QgsGdalUtils::resampleSingleBandRaster( srcDS.get(), dstDS.get(), GRA_NearestNeighbour, nullptr );
182 dstDS.reset();
183
184 std::unique_ptr<QgsRasterLayer> layer( new QgsRasterLayer( outputFilename, "test", "gdal" ) );
185 QVERIFY( layer );
186 std::unique_ptr<QgsRasterBlock> block( layer->dataProvider()->block( 1, outputExtent, 2, 2 ) );
187 QVERIFY( block );
188 QCOMPARE( block->value( 0, 0 ), 6. );
189 QCOMPARE( block->value( 1, 1 ), 11. );
190
191 layer.reset();
192 QFile::remove( outputFilename );
193 }
194
testImageToDataset()195 void TestQgsGdalUtils::testImageToDataset()
196 {
197 const QString inputFilename = QString( TEST_DATA_DIR ) + "/rgb256x256.png";
198 QImage src = QImage( inputFilename );
199 src = src.convertToFormat( QImage::Format_ARGB32 );
200 QVERIFY( !src.isNull() );
201
202 gdal::dataset_unique_ptr dstDS = QgsGdalUtils::imageToMemoryDataset( QImage() );
203 QVERIFY( !dstDS );
204
205 dstDS = QgsGdalUtils::imageToMemoryDataset( src );
206 QVERIFY( dstDS );
207
208 QCOMPARE( GDALGetRasterCount( dstDS.get() ), 4 );
209 QCOMPARE( GDALGetRasterXSize( dstDS.get() ), 256 );
210 QCOMPARE( GDALGetRasterYSize( dstDS.get() ), 256 );
211
212 QCOMPARE( GDALGetRasterDataType( GDALGetRasterBand( dstDS.get(), 1 ) ), GDT_Byte );
213 QCOMPARE( GDALGetRasterDataType( GDALGetRasterBand( dstDS.get(), 2 ) ), GDT_Byte );
214 QCOMPARE( GDALGetRasterDataType( GDALGetRasterBand( dstDS.get(), 3 ) ), GDT_Byte );
215 QCOMPARE( GDALGetRasterDataType( GDALGetRasterBand( dstDS.get(), 4 ) ), GDT_Byte );
216
217 QCOMPARE( identify( dstDS.get(), 1, 50, 50 ), 255.0 );
218 QCOMPARE( identify( dstDS.get(), 1, 200, 50 ), 255.0 );
219 QCOMPARE( identify( dstDS.get(), 1, 50, 200 ), 0.0 );
220 QCOMPARE( identify( dstDS.get(), 1, 200, 200 ), 0.0 );
221
222 QCOMPARE( identify( dstDS.get(), 2, 50, 50 ), 255.0 );
223 QCOMPARE( identify( dstDS.get(), 2, 200, 50 ), 0.0 );
224 QCOMPARE( identify( dstDS.get(), 2, 50, 200 ), 255.0 );
225 QCOMPARE( identify( dstDS.get(), 2, 200, 200 ), 0.0 );
226
227 QCOMPARE( identify( dstDS.get(), 3, 50, 50 ), 0.0 );
228 QCOMPARE( identify( dstDS.get(), 3, 200, 50 ), 0.0 );
229 QCOMPARE( identify( dstDS.get(), 3, 50, 200 ), 0.0 );
230 QCOMPARE( identify( dstDS.get(), 3, 200, 200 ), 255.0 );
231
232 QCOMPARE( identify( dstDS.get(), 4, 50, 50 ), 255.0 );
233 QCOMPARE( identify( dstDS.get(), 4, 200, 50 ), 255.0 );
234 QCOMPARE( identify( dstDS.get(), 4, 50, 200 ), 255.0 );
235 QCOMPARE( identify( dstDS.get(), 4, 200, 200 ), 255.0 );
236 }
237
testResampleImageToImage()238 void TestQgsGdalUtils::testResampleImageToImage()
239 {
240 const QString inputFilename = QString( TEST_DATA_DIR ) + "/rgb256x256.png";
241 QImage src = QImage( inputFilename );
242 src = src.convertToFormat( QImage::Format_ARGB32 );
243 QVERIFY( !src.isNull() );
244
245 QImage res = QgsGdalUtils::resampleImage( QImage(), QSize( 50, 50 ), GRIORA_NearestNeighbour );
246 QVERIFY( res.isNull() );
247
248 res = QgsGdalUtils::resampleImage( src, QSize( 50, 50 ), GRIORA_NearestNeighbour );
249 QVERIFY( !res.isNull() );
250 QCOMPARE( res.width(), 50 );
251 QCOMPARE( res.height(), 50 );
252
253 QCOMPARE( qRed( res.pixel( 10, 10 ) ), 255 );
254 QCOMPARE( qGreen( res.pixel( 10, 10 ) ), 255 );
255 QCOMPARE( qBlue( res.pixel( 10, 10 ) ), 0 );
256 QCOMPARE( qAlpha( res.pixel( 10, 10 ) ), 255 );
257
258 QCOMPARE( qRed( res.pixel( 40, 10 ) ), 255 );
259 QCOMPARE( qGreen( res.pixel( 40, 10 ) ), 0 );
260 QCOMPARE( qBlue( res.pixel( 40, 10 ) ), 0 );
261 QCOMPARE( qAlpha( res.pixel( 40, 10 ) ), 255 );
262
263 QCOMPARE( qRed( res.pixel( 10, 40 ) ), 0 );
264 QCOMPARE( qGreen( res.pixel( 10, 40 ) ), 255 );
265 QCOMPARE( qBlue( res.pixel( 10, 40 ) ), 0 );
266 QCOMPARE( qAlpha( res.pixel( 10, 40 ) ), 255 );
267
268 QCOMPARE( qRed( res.pixel( 40, 40 ) ), 0 );
269 QCOMPARE( qGreen( res.pixel( 40, 40 ) ), 0 );
270 QCOMPARE( qBlue( res.pixel( 40, 40 ) ), 255 );
271 QCOMPARE( qAlpha( res.pixel( 40, 40 ) ), 255 );
272 }
273
testPathIsCheapToOpen()274 void TestQgsGdalUtils::testPathIsCheapToOpen()
275 {
276 // should be safe and report false paths which don't exist
277 QVERIFY( !QgsGdalUtils::pathIsCheapToOpen( "/not/a/file" ) );
278
279 // for now, tiff aren't marked as cheap to open
280 QVERIFY( !QgsGdalUtils::pathIsCheapToOpen( QStringLiteral( TEST_DATA_DIR ) + "/big_raster.tif" ) );
281
282 // a csv is considered cheap to open
283 QVERIFY( QgsGdalUtils::pathIsCheapToOpen( QStringLiteral( TEST_DATA_DIR ) + "/delimitedtext/test.csv" ) );
284
285 // ... unless it's larger than the specified file size limit (500 bytes)
286 QVERIFY( !QgsGdalUtils::pathIsCheapToOpen( QStringLiteral( TEST_DATA_DIR ) + "/delimitedtext/testdms.csv", 500 ) );
287 }
288
testVrtMatchesLayerType()289 void TestQgsGdalUtils::testVrtMatchesLayerType()
290 {
291 QVERIFY( QgsGdalUtils::vrtMatchesLayerType( QStringLiteral( TEST_DATA_DIR ) + "/raster/hub13263.vrt", QgsMapLayerType::RasterLayer ) );
292 QVERIFY( !QgsGdalUtils::vrtMatchesLayerType( QStringLiteral( TEST_DATA_DIR ) + "/raster/hub13263.vrt", QgsMapLayerType::VectorLayer ) );
293
294 QVERIFY( !QgsGdalUtils::vrtMatchesLayerType( QStringLiteral( TEST_DATA_DIR ) + "/vector_vrt.vrt", QgsMapLayerType::RasterLayer ) );
295 QVERIFY( QgsGdalUtils::vrtMatchesLayerType( QStringLiteral( TEST_DATA_DIR ) + "/vector_vrt.vrt", QgsMapLayerType::VectorLayer ) );
296 }
297
testMultilayerExtensions()298 void TestQgsGdalUtils::testMultilayerExtensions()
299 {
300 const QStringList extensions = QgsGdalUtils::multiLayerFileExtensions();
301 QVERIFY( extensions.contains( QStringLiteral( "gpkg" ) ) );
302 QVERIFY( extensions.contains( QStringLiteral( "sqlite" ) ) );
303 QVERIFY( extensions.contains( QStringLiteral( "db" ) ) );
304 QVERIFY( extensions.contains( QStringLiteral( "kml" ) ) );
305 QVERIFY( extensions.contains( QStringLiteral( "ods" ) ) );
306 QVERIFY( extensions.contains( QStringLiteral( "osm" ) ) );
307 QVERIFY( extensions.contains( QStringLiteral( "mdb" ) ) );
308 QVERIFY( extensions.contains( QStringLiteral( "xls" ) ) );
309 QVERIFY( extensions.contains( QStringLiteral( "xlsx" ) ) );
310 QVERIFY( extensions.contains( QStringLiteral( "gpx" ) ) );
311 QVERIFY( extensions.contains( QStringLiteral( "pdf" ) ) );
312 QVERIFY( extensions.contains( QStringLiteral( "nc" ) ) );
313 QVERIFY( extensions.contains( QStringLiteral( "gdb" ) ) );
314 }
315
identify(GDALDatasetH dataset,int band,int px,int py)316 double TestQgsGdalUtils::identify( GDALDatasetH dataset, int band, int px, int py )
317 {
318 GDALRasterBandH hBand = GDALGetRasterBand( dataset, band );
319
320 float *pafScanline = ( float * ) CPLMalloc( sizeof( float ) );
321 const CPLErr err = GDALRasterIO( hBand, GF_Read, px, py, 1, 1,
322 pafScanline, 1, 1, GDT_Float32, 0, 0 );
323 const double value = err == CE_None ? pafScanline[0] : std::numeric_limits<double>::quiet_NaN();
324 CPLFree( pafScanline );
325
326 return value;
327 }
328
329 QGSTEST_MAIN( TestQgsGdalUtils )
330 #include "testqgsgdalutils.moc"
331