Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsgdalproviderbase.cpp - Common base class for GDAL and WCS provider
3 : : -------------------
4 : : begin : November, 2010
5 : : copyright : (C) 2010 by Radim Blazek
6 : : email : radim dot blazek at gmail dot com
7 : : ***************************************************************************/
8 : :
9 : : /***************************************************************************
10 : : * *
11 : : * This program is free software; you can redistribute it and/or modify *
12 : : * it under the terms of the GNU General Public License as published by *
13 : : * the Free Software Foundation; either version 2 of the License, or *
14 : : * (at your option) any later version. *
15 : : * *
16 : : ***************************************************************************/
17 : :
18 : : #include "qgsgdalproviderbase.h"
19 : : ///@cond PRIVATE
20 : :
21 : : #define CPL_SUPRESS_CPLUSPLUS //#spellok
22 : : #include <cpl_conv.h>
23 : : #include <cpl_string.h>
24 : :
25 : : #include "qgsapplication.h"
26 : : #include "qgslogger.h"
27 : : #include "qgsgdalproviderbase.h"
28 : : #include "qgssettings.h"
29 : :
30 : : #include <mutex>
31 : : #include <QRegularExpression>
32 : : #include <QRegularExpressionMatch>
33 : :
34 : 0 : QgsGdalProviderBase::QgsGdalProviderBase()
35 : : {
36 : :
37 : : // first get the GDAL driver manager
38 : 0 : QgsGdalProviderBase::registerGdalDrivers();
39 : 0 : }
40 : :
41 : : /**
42 : : * \param bandNumber the number of the band for which you want a color table
43 : : * \param list a pointer the object that will hold the color table
44 : : * \return TRUE of a color table was able to be read, FALSE otherwise
45 : : */
46 : 0 : QList<QgsColorRampShader::ColorRampItem> QgsGdalProviderBase::colorTable( GDALDatasetH gdalDataset, int bandNumber )const
47 : : {
48 : 0 : QList<QgsColorRampShader::ColorRampItem> ct;
49 : :
50 : : //Invalid band number, segfault prevention
51 : 0 : if ( 0 >= bandNumber )
52 : : {
53 : 0 : QgsDebugMsg( QStringLiteral( "Invalid parameter" ) );
54 : 0 : return ct;
55 : : }
56 : :
57 : 0 : GDALRasterBandH myGdalBand = GDALGetRasterBand( gdalDataset, bandNumber );
58 : 0 : GDALColorTableH myGdalColorTable = GDALGetRasterColorTable( myGdalBand );
59 : :
60 : 0 : if ( myGdalColorTable )
61 : : {
62 : 0 : QgsDebugMsgLevel( QStringLiteral( "Color table found" ), 2 );
63 : :
64 : : // load category labels
65 : 0 : char **categoryNames = GDALGetRasterCategoryNames( myGdalBand );
66 : 0 : QVector<QString> labels;
67 : 0 : if ( categoryNames )
68 : : {
69 : 0 : int i = 0;
70 : 0 : while ( categoryNames[i] )
71 : : {
72 : 0 : labels.append( QString( categoryNames[i] ) );
73 : 0 : i++;
74 : : }
75 : 0 : }
76 : :
77 : 0 : int myEntryCount = GDALGetColorEntryCount( myGdalColorTable );
78 : 0 : GDALColorInterp myColorInterpretation = GDALGetRasterColorInterpretation( myGdalBand );
79 : 0 : QgsDebugMsgLevel( "Color Interpretation: " + QString::number( static_cast< int >( myColorInterpretation ) ), 2 );
80 : 0 : GDALPaletteInterp myPaletteInterpretation = GDALGetPaletteInterpretation( myGdalColorTable );
81 : 0 : QgsDebugMsgLevel( "Palette Interpretation: " + QString::number( static_cast< int >( myPaletteInterpretation ) ), 2 );
82 : :
83 : 0 : const GDALColorEntry *myColorEntry = nullptr;
84 : 0 : for ( int myIterator = 0; myIterator < myEntryCount; myIterator++ )
85 : : {
86 : 0 : myColorEntry = GDALGetColorEntry( myGdalColorTable, myIterator );
87 : :
88 : 0 : if ( !myColorEntry )
89 : : {
90 : 0 : continue;
91 : : }
92 : : else
93 : : {
94 : 0 : QString label = labels.value( myIterator );
95 : 0 : if ( label.isEmpty() )
96 : : {
97 : 0 : label = QString::number( myIterator );
98 : 0 : }
99 : : //Branch on the color interpretation type
100 : 0 : if ( myColorInterpretation == GCI_GrayIndex )
101 : : {
102 : 0 : QgsColorRampShader::ColorRampItem myColorRampItem;
103 : 0 : myColorRampItem.value = static_cast< double >( myIterator );
104 : 0 : myColorRampItem.label = label;
105 : 0 : myColorRampItem.color = QColor::fromRgb( myColorEntry->c1, myColorEntry->c1, myColorEntry->c1, myColorEntry->c4 );
106 : 0 : ct.append( myColorRampItem );
107 : 0 : }
108 : 0 : else if ( myColorInterpretation == GCI_PaletteIndex )
109 : : {
110 : 0 : QgsColorRampShader::ColorRampItem myColorRampItem;
111 : 0 : myColorRampItem.value = static_cast< double >( myIterator );
112 : 0 : myColorRampItem.label = label;
113 : : //Branch on palette interpretation
114 : 0 : if ( myPaletteInterpretation == GPI_RGB )
115 : : {
116 : 0 : myColorRampItem.color = QColor::fromRgb( myColorEntry->c1, myColorEntry->c2, myColorEntry->c3, myColorEntry->c4 );
117 : 0 : }
118 : 0 : else if ( myPaletteInterpretation == GPI_CMYK )
119 : : {
120 : 0 : myColorRampItem.color = QColor::fromCmyk( myColorEntry->c1, myColorEntry->c2, myColorEntry->c3, myColorEntry->c4 );
121 : 0 : }
122 : 0 : else if ( myPaletteInterpretation == GPI_HLS )
123 : : {
124 : 0 : myColorRampItem.color = QColor::fromHsv( myColorEntry->c1, myColorEntry->c3, myColorEntry->c2, myColorEntry->c4 );
125 : 0 : }
126 : : else
127 : : {
128 : 0 : myColorRampItem.color = QColor::fromRgb( myColorEntry->c1, myColorEntry->c1, myColorEntry->c1, myColorEntry->c4 );
129 : : }
130 : 0 : ct.append( myColorRampItem );
131 : 0 : }
132 : : else
133 : : {
134 : 0 : QgsDebugMsgLevel( QStringLiteral( "Color interpretation type not supported yet" ), 2 );
135 : 0 : return ct;
136 : : }
137 : 0 : }
138 : 0 : }
139 : 0 : }
140 : : else
141 : : {
142 : 0 : QgsDebugMsgLevel( "No color table found for band " + QString::number( bandNumber ), 2 );
143 : 0 : return ct;
144 : : }
145 : :
146 : 0 : QgsDebugMsgLevel( QStringLiteral( "Color table loaded successfully" ), 2 );
147 : 0 : return ct;
148 : 0 : }
149 : :
150 : 0 : Qgis::DataType QgsGdalProviderBase::dataTypeFromGdal( const GDALDataType gdalDataType ) const
151 : : {
152 : 0 : switch ( gdalDataType )
153 : : {
154 : : case GDT_Byte:
155 : 0 : return Qgis::Byte;
156 : : case GDT_UInt16:
157 : 0 : return Qgis::UInt16;
158 : : case GDT_Int16:
159 : 0 : return Qgis::Int16;
160 : : case GDT_UInt32:
161 : 0 : return Qgis::UInt32;
162 : : case GDT_Int32:
163 : 0 : return Qgis::Int32;
164 : : case GDT_Float32:
165 : 0 : return Qgis::Float32;
166 : : case GDT_Float64:
167 : 0 : return Qgis::Float64;
168 : : case GDT_CInt16:
169 : 0 : return Qgis::CInt16;
170 : : case GDT_CInt32:
171 : 0 : return Qgis::CInt32;
172 : : case GDT_CFloat32:
173 : 0 : return Qgis::CFloat32;
174 : : case GDT_CFloat64:
175 : 0 : return Qgis::CFloat64;
176 : : case GDT_Unknown:
177 : : case GDT_TypeCount:
178 : 0 : return Qgis::UnknownDataType;
179 : : }
180 : 0 : return Qgis::UnknownDataType;
181 : 0 : }
182 : :
183 : 0 : int QgsGdalProviderBase::colorInterpretationFromGdal( const GDALColorInterp gdalColorInterpretation ) const
184 : : {
185 : 0 : switch ( gdalColorInterpretation )
186 : : {
187 : : case GCI_GrayIndex:
188 : 0 : return QgsRaster::GrayIndex;
189 : : case GCI_PaletteIndex:
190 : 0 : return QgsRaster::PaletteIndex;
191 : : case GCI_RedBand:
192 : 0 : return QgsRaster::RedBand;
193 : : case GCI_GreenBand:
194 : 0 : return QgsRaster::GreenBand;
195 : : case GCI_BlueBand:
196 : 0 : return QgsRaster::BlueBand;
197 : : case GCI_AlphaBand:
198 : 0 : return QgsRaster::AlphaBand;
199 : : case GCI_HueBand:
200 : 0 : return QgsRaster::HueBand;
201 : : case GCI_SaturationBand:
202 : 0 : return QgsRaster::SaturationBand;
203 : : case GCI_LightnessBand:
204 : 0 : return QgsRaster::LightnessBand;
205 : : case GCI_CyanBand:
206 : 0 : return QgsRaster::CyanBand;
207 : : case GCI_MagentaBand:
208 : 0 : return QgsRaster::MagentaBand;
209 : : case GCI_YellowBand:
210 : 0 : return QgsRaster::YellowBand;
211 : : case GCI_BlackBand:
212 : 0 : return QgsRaster::BlackBand;
213 : : case GCI_YCbCr_YBand:
214 : 0 : return QgsRaster::YCbCr_YBand;
215 : : case GCI_YCbCr_CbBand:
216 : 0 : return QgsRaster::YCbCr_CbBand;
217 : : case GCI_YCbCr_CrBand:
218 : 0 : return QgsRaster::YCbCr_CrBand;
219 : : case GCI_Undefined:
220 : 0 : return QgsRaster::UndefinedColorInterpretation;
221 : : }
222 : 0 : return QgsRaster::UndefinedColorInterpretation;
223 : 0 : }
224 : :
225 : 3 : void QgsGdalProviderBase::registerGdalDrivers()
226 : : {
227 : : static std::once_flag initialized;
228 : 3 : std::call_once( initialized, QgsApplication::registerGdalDriversFromSettings );
229 : 3 : }
230 : :
231 : 0 : QgsRectangle QgsGdalProviderBase::extent( GDALDatasetH gdalDataset )const
232 : : {
233 : : double myGeoTransform[6];
234 : :
235 : 0 : bool myHasGeoTransform = GDALGetGeoTransform( gdalDataset, myGeoTransform ) == CE_None;
236 : 0 : if ( !myHasGeoTransform )
237 : : {
238 : : // Initialize the affine transform matrix
239 : 0 : myGeoTransform[0] = 0;
240 : 0 : myGeoTransform[1] = 1;
241 : 0 : myGeoTransform[2] = 0;
242 : 0 : myGeoTransform[3] = 0;
243 : 0 : myGeoTransform[4] = 0;
244 : 0 : myGeoTransform[5] = -1;
245 : 0 : }
246 : :
247 : : // Use the affine transform to get geo coordinates for
248 : : // the corners of the raster
249 : 0 : double myXMax = myGeoTransform[0] +
250 : 0 : GDALGetRasterXSize( gdalDataset ) * myGeoTransform[1] +
251 : 0 : GDALGetRasterYSize( gdalDataset ) * myGeoTransform[2];
252 : 0 : double myYMin = myGeoTransform[3] +
253 : 0 : GDALGetRasterXSize( gdalDataset ) * myGeoTransform[4] +
254 : 0 : GDALGetRasterYSize( gdalDataset ) * myGeoTransform[5];
255 : :
256 : 0 : QgsRectangle extent( myGeoTransform[0], myYMin, myXMax, myGeoTransform[3] );
257 : 0 : return extent;
258 : : }
259 : :
260 : 0 : GDALDatasetH QgsGdalProviderBase::gdalOpen( const QString &uri, unsigned int nOpenFlags )
261 : : {
262 : 0 : QVariantMap parts = decodeGdalUri( uri );
263 : 0 : const QStringList openOptions = parts.value( QStringLiteral( "openOptions" ) ).toStringList();
264 : 0 : parts.remove( QStringLiteral( "openOptions" ) );
265 : :
266 : 0 : char **papszOpenOptions = nullptr;
267 : 0 : for ( const QString &option : openOptions )
268 : : {
269 : 0 : papszOpenOptions = CSLAddString( papszOpenOptions,
270 : 0 : option.toUtf8().constData() );
271 : : }
272 : :
273 : 0 : bool modify_OGR_GPKG_FOREIGN_KEY_CHECK = !CPLGetConfigOption( "OGR_GPKG_FOREIGN_KEY_CHECK", nullptr );
274 : 0 : if ( modify_OGR_GPKG_FOREIGN_KEY_CHECK )
275 : : {
276 : 0 : CPLSetThreadLocalConfigOption( "OGR_GPKG_FOREIGN_KEY_CHECK", "NO" );
277 : 0 : }
278 : :
279 : 0 : GDALDatasetH hDS = GDALOpenEx( encodeGdalUri( parts ).toUtf8().constData(), nOpenFlags, nullptr, papszOpenOptions, nullptr );
280 : 0 : CSLDestroy( papszOpenOptions );
281 : :
282 : 0 : if ( modify_OGR_GPKG_FOREIGN_KEY_CHECK )
283 : : {
284 : 0 : CPLSetThreadLocalConfigOption( "OGR_GPKG_FOREIGN_KEY_CHECK", nullptr );
285 : 0 : }
286 : 0 : return hDS;
287 : 0 : }
288 : :
289 : 0 : int CPL_STDCALL _gdalProgressFnWithFeedback( double dfComplete, const char *pszMessage, void *pProgressArg )
290 : : {
291 : : Q_UNUSED( dfComplete )
292 : : Q_UNUSED( pszMessage )
293 : :
294 : 0 : QgsRasterBlockFeedback *feedback = static_cast<QgsRasterBlockFeedback *>( pProgressArg );
295 : 0 : return !feedback->isCanceled();
296 : : }
297 : :
298 : :
299 : 0 : CPLErr QgsGdalProviderBase::gdalRasterIO( GDALRasterBandH hBand, GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize, void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType, int nPixelSpace, int nLineSpace, QgsRasterBlockFeedback *feedback )
300 : : {
301 : : GDALRasterIOExtraArg extra;
302 : 0 : INIT_RASTERIO_EXTRA_ARG( extra );
303 : : if ( false && feedback ) // disabled!
304 : : {
305 : : // Currently the cancellation is disabled... When RasterIO call is canceled,
306 : : // GDAL returns CE_Failure with error code = 0 (CPLE_None), however one would
307 : : // expect to get CPLE_UserInterrupt to clearly identify that the failure was
308 : : // caused by the cancellation and not that something dodgy is going on.
309 : : // Are both error codes acceptable?
310 : : extra.pfnProgress = _gdalProgressFnWithFeedback;
311 : : extra.pProgressData = ( void * ) feedback;
312 : : }
313 : 0 : CPLErr err = GDALRasterIOEx( hBand, eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, eBufType, nPixelSpace, nLineSpace, &extra );
314 : :
315 : 0 : return err;
316 : : }
317 : :
318 : 0 : int QgsGdalProviderBase::gdalGetOverviewCount( GDALRasterBandH hBand )
319 : : {
320 : 0 : int count = GDALGetOverviewCount( hBand );
321 : 0 : return count;
322 : : }
323 : :
324 : 0 : QVariantMap QgsGdalProviderBase::decodeGdalUri( const QString &uri )
325 : : {
326 : 0 : QString path = uri;
327 : 0 : QString layerName;
328 : 0 : QStringList openOptions;
329 : :
330 : 0 : QString vsiPrefix = qgsVsiPrefix( path );
331 : 0 : QString vsiSuffix;
332 : 0 : if ( path.startsWith( vsiPrefix, Qt::CaseInsensitive ) )
333 : : {
334 : 0 : path = path.mid( vsiPrefix.count() );
335 : 0 : if ( vsiPrefix == QLatin1String( "/vsizip/" ) )
336 : : {
337 : 0 : const QRegularExpression vsiRegex( QStringLiteral( "(?:\\.zip|\\.tar|\\.gz|\\.tar\\.gz|\\.tgz)([^|]*)" ) );
338 : 0 : QRegularExpressionMatch match = vsiRegex.match( path );
339 : 0 : if ( match.hasMatch() )
340 : : {
341 : 0 : vsiSuffix = match.captured( 1 );
342 : 0 : path = path.remove( match.capturedStart( 1 ), match.capturedLength( 1 ) );
343 : 0 : }
344 : 0 : }
345 : 0 : }
346 : : else
347 : : {
348 : 0 : vsiPrefix.clear();
349 : : }
350 : :
351 : 0 : if ( path.indexOf( ':' ) != -1 )
352 : : {
353 : 0 : QStringList parts = path.split( ':' );
354 : 0 : if ( parts[0].toLower() == QLatin1String( "gpkg" ) )
355 : : {
356 : 0 : parts.removeFirst();
357 : : // Handle windows paths - which has an extra colon - and unix paths
358 : 0 : if ( ( parts[0].length() > 1 && parts.count() > 1 ) || parts.count() > 2 )
359 : : {
360 : 0 : layerName = parts[parts.length() - 1];
361 : 0 : parts.removeLast();
362 : 0 : }
363 : 0 : path = parts.join( ':' );
364 : 0 : }
365 : 0 : }
366 : :
367 : 0 : if ( path.contains( '|' ) )
368 : : {
369 : 0 : const QRegularExpression openOptionRegex( QStringLiteral( "\\|option:([^|]*)" ) );
370 : 0 : while ( true )
371 : : {
372 : 0 : QRegularExpressionMatch match = openOptionRegex.match( path );
373 : 0 : if ( match.hasMatch() )
374 : : {
375 : 0 : openOptions << match.captured( 1 );
376 : 0 : path = path.remove( match.capturedStart( 0 ), match.capturedLength( 0 ) );
377 : 0 : }
378 : : else
379 : : {
380 : 0 : break;
381 : : }
382 : 0 : }
383 : 0 : }
384 : :
385 : 0 : QVariantMap uriComponents;
386 : 0 : uriComponents.insert( QStringLiteral( "path" ), path );
387 : 0 : uriComponents.insert( QStringLiteral( "layerName" ), layerName );
388 : 0 : if ( !openOptions.isEmpty() )
389 : 0 : uriComponents.insert( QStringLiteral( "openOptions" ), openOptions );
390 : 0 : if ( !vsiPrefix.isEmpty() )
391 : 0 : uriComponents.insert( QStringLiteral( "vsiPrefix" ), vsiPrefix );
392 : 0 : if ( !vsiSuffix.isEmpty() )
393 : 0 : uriComponents.insert( QStringLiteral( "vsiSuffix" ), vsiSuffix );
394 : 0 : return uriComponents;
395 : 0 : }
396 : :
397 : 0 : QString QgsGdalProviderBase::encodeGdalUri( const QVariantMap &parts )
398 : : {
399 : 0 : const QString vsiPrefix = parts.value( QStringLiteral( "vsiPrefix" ) ).toString();
400 : 0 : const QString vsiSuffix = parts.value( QStringLiteral( "vsiSuffix" ) ).toString();
401 : 0 : const QString path = parts.value( QStringLiteral( "path" ) ).toString();
402 : 0 : const QString layerName = parts.value( QStringLiteral( "layerName" ) ).toString();
403 : :
404 : 0 : QString uri = vsiPrefix + path + vsiSuffix;
405 : 0 : if ( !layerName.isEmpty() && uri.endsWith( QLatin1String( "gpkg" ) ) )
406 : 0 : uri = QStringLiteral( "GPKG:%1:%2" ).arg( uri, layerName );
407 : 0 : else if ( !layerName.isEmpty() )
408 : 0 : uri = uri + QStringLiteral( "|%1" ).arg( layerName );
409 : :
410 : 0 : const QStringList openOptions = parts.value( QStringLiteral( "openOptions" ) ).toStringList();
411 : :
412 : 0 : for ( const QString &openOption : openOptions )
413 : : {
414 : 0 : uri += QLatin1String( "|option:" );
415 : 0 : uri += openOption;
416 : : }
417 : :
418 : 0 : return uri;
419 : 0 : }
420 : :
421 : : ///@endcond
|