mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-16 00:03:12 -04:00
6012 lines
206 KiB
C++
6012 lines
206 KiB
C++
/***************************************************************************
|
|
qgsrasterlayer.cpp - description
|
|
-------------------
|
|
begin : Sat Jun 22 2002
|
|
copyright : (C) 2003 by Tim Sutton, Steve Halasz and Gary E.Sherman
|
|
email : tim at linfiniti.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. *
|
|
* *
|
|
***************************************************************************/
|
|
/* $Id$ */
|
|
|
|
#include "qgsapplication.h"
|
|
#include "qgslogger.h"
|
|
#include "qgsmaplayerregistry.h"
|
|
#include "qgsmaptopixel.h"
|
|
#include "qgsproviderregistry.h"
|
|
#include "qgsrasterbandstats.h"
|
|
#include "qgsrasterlayer.h"
|
|
#include "qgsrasterpyramid.h"
|
|
#include "qgsrectangle.h"
|
|
#include "qgsrendercontext.h"
|
|
#include "qgscoordinatereferencesystem.h"
|
|
|
|
#include "gdalwarper.h"
|
|
#include "cpl_conv.h"
|
|
|
|
#include "qgspseudocolorshader.h"
|
|
#include "qgsfreakoutshader.h"
|
|
#include "qgscolorrampshader.h"
|
|
|
|
#include <cstdio>
|
|
#include <cmath>
|
|
#include <limits>
|
|
|
|
#include <QApplication>
|
|
#include <QCursor>
|
|
#include <QDomElement>
|
|
#include <QDomNode>
|
|
#include <QFile>
|
|
#include <QFileInfo>
|
|
#include <QFont>
|
|
#include <QFontMetrics>
|
|
#include <QFrame>
|
|
#include <QImage>
|
|
#include <QLabel>
|
|
#include <QList>
|
|
#include <QMatrix>
|
|
#include <QMessageBox>
|
|
#include <QLibrary>
|
|
#include <QPainter>
|
|
#include <QPixmap>
|
|
#include <QRegExp>
|
|
#include <QSlider>
|
|
#include <QSettings>
|
|
#include "qgslogger.h"
|
|
// workaround for MSVC compiler which already has defined macro max
|
|
// that interferes with calling std::numeric_limits<int>::max
|
|
#ifdef _MSC_VER
|
|
# ifdef max
|
|
# undef max
|
|
# endif
|
|
#endif
|
|
|
|
// Comparison value for equality; i.e., we shouldn't directly compare two
|
|
// floats so it's better to take their difference and see if they're within
|
|
// a certain range -- in this case twenty times the smallest value that
|
|
// doubles can take for the current system. (Yes, 20 was arbitrary.)
|
|
#define TINY_VALUE std::numeric_limits<double>::epsilon() * 20
|
|
|
|
#if defined(GDAL_VERSION_NUM) && GDAL_VERSION_NUM >= 1800
|
|
#define TO8F(x) (x).toUtf8().constData()
|
|
#else
|
|
#define TO8F(x) QFile::encodeName( x ).constData()
|
|
#endif
|
|
|
|
QgsRasterLayer::QgsRasterLayer(
|
|
QString const & path,
|
|
QString const & baseName,
|
|
bool loadDefaultStyleFlag )
|
|
: QgsMapLayer( RasterLayer, baseName, path )
|
|
// Constant that signals property not used.
|
|
, QSTRING_NOT_SET( "Not Set" )
|
|
, TRSTRING_NOT_SET( tr( "Not Set" ) )
|
|
, mStandardDeviations( 0 )
|
|
, mDataProvider( 0 )
|
|
, mWidth( std::numeric_limits<int>::max() )
|
|
, mHeight( std::numeric_limits<int>::max() )
|
|
, mInvertColor( false )
|
|
{
|
|
|
|
mRasterType = QgsRasterLayer::GrayOrUndefined;
|
|
|
|
mRedBandName = TRSTRING_NOT_SET;
|
|
mGreenBandName = TRSTRING_NOT_SET;
|
|
mBlueBandName = TRSTRING_NOT_SET;
|
|
mGrayBandName = TRSTRING_NOT_SET;
|
|
mTransparencyBandName = TRSTRING_NOT_SET;
|
|
|
|
|
|
mUserDefinedRGBMinimumMaximum = false; //defaults needed to bypass enhanceContrast
|
|
mUserDefinedGrayMinimumMaximum = false;
|
|
mRGBMinimumMaximumEstimated = true;
|
|
mGrayMinimumMaximumEstimated = true;
|
|
|
|
mDrawingStyle = QgsRasterLayer::UndefinedDrawingStyle;
|
|
mContrastEnhancementAlgorithm = QgsContrastEnhancement::NoEnhancement;
|
|
mColorShadingAlgorithm = QgsRasterLayer::UndefinedShader;
|
|
mRasterShader = new QgsRasterShader();
|
|
|
|
mBandCount = 0;
|
|
mHasPyramids = false;
|
|
mNoDataValue = -9999.0;
|
|
mValidNoDataValue = false;
|
|
|
|
mGdalBaseDataset = 0;
|
|
mGdalDataset = 0;
|
|
|
|
// Initialise the affine transform matrix
|
|
mGeoTransform[0] = 0;
|
|
mGeoTransform[1] = 1;
|
|
mGeoTransform[2] = 0;
|
|
mGeoTransform[3] = 0;
|
|
mGeoTransform[4] = 0;
|
|
mGeoTransform[5] = -1;
|
|
|
|
// set the layer name (uppercase first character)
|
|
|
|
if ( ! baseName.isEmpty() ) // XXX shouldn't this happen in parent?
|
|
{
|
|
setLayerName( baseName );
|
|
}
|
|
|
|
// load the file if one specified
|
|
if ( ! path.isEmpty() )
|
|
{
|
|
readFile( path ); // XXX check for failure?
|
|
|
|
//readFile() is really an extension of the constructor as many imporant fields are set in this method
|
|
//loadDefaultStyle() can not be called before the layer has actually be opened
|
|
if ( loadDefaultStyleFlag )
|
|
{
|
|
bool defaultLoadedFlag = false;
|
|
loadDefaultStyle( defaultLoadedFlag );
|
|
if ( defaultLoadedFlag )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Initialize the last view port structure, should really be a class
|
|
mLastViewPort.rectXOffset = 0;
|
|
mLastViewPort.rectXOffsetFloat = 0.0;
|
|
mLastViewPort.rectYOffset = 0;
|
|
mLastViewPort.rectYOffsetFloat = 0.0;
|
|
mLastViewPort.clippedXMin = 0.0;
|
|
mLastViewPort.clippedXMax = 0.0;
|
|
mLastViewPort.clippedYMin = 0.0;
|
|
mLastViewPort.clippedYMax = 0.0;
|
|
mLastViewPort.clippedWidth = 0;
|
|
mLastViewPort.clippedHeight = 0;
|
|
mLastViewPort.drawableAreaXDim = 0;
|
|
mLastViewPort.drawableAreaYDim = 0;
|
|
|
|
} // QgsRasterLayer ctor
|
|
|
|
/**
|
|
* @todo Rename into a general constructor when the old raster interface is retired
|
|
* parameter dummy is just there to distinguish this function signature from the old non-provider one.
|
|
*/
|
|
QgsRasterLayer::QgsRasterLayer( int dummy,
|
|
QString const & rasterLayerPath,
|
|
QString const & baseName,
|
|
QString const & providerKey,
|
|
QStringList const & layers,
|
|
QStringList const & styles,
|
|
QString const & format,
|
|
QString const & crs )
|
|
: QgsMapLayer( RasterLayer, baseName, rasterLayerPath )
|
|
, mStandardDeviations( 0 )
|
|
, mDataProvider( 0 )
|
|
, mEditable( false )
|
|
, mWidth( std::numeric_limits<int>::max() )
|
|
, mHeight( std::numeric_limits<int>::max() )
|
|
, mInvertColor( false )
|
|
, mModified( false )
|
|
, mProviderKey( providerKey )
|
|
{
|
|
QgsDebugMsg( "(8 arguments) starting. with layer list of " +
|
|
layers.join( ", " ) + " and style list of " + styles.join( ", " ) + " and format of " +
|
|
format + " and CRS of " + crs );
|
|
|
|
mBandCount = 0;
|
|
mRasterShader = new QgsRasterShader();
|
|
|
|
// Initialise the affine transform matrix
|
|
mGeoTransform[0] = 0;
|
|
mGeoTransform[1] = 1;
|
|
mGeoTransform[2] = 0;
|
|
mGeoTransform[3] = 0;
|
|
mGeoTransform[4] = 0;
|
|
mGeoTransform[5] = -1;
|
|
|
|
// if we're given a provider type, try to create and bind one to this layer
|
|
if ( ! providerKey.isEmpty() )
|
|
{
|
|
setDataProvider( providerKey, layers, styles, format, crs );
|
|
}
|
|
|
|
// Default for the popup menu
|
|
// TODO: popMenu = 0;
|
|
|
|
// Get the update threshold from user settings. We
|
|
// do this only on construction to avoid the penality of
|
|
// fetching this each time the layer is drawn. If the user
|
|
// changes the threshold from the preferences dialog, it will
|
|
// have no effect on existing layers
|
|
// TODO: QSettings settings;
|
|
// updateThreshold = settings.readNumEntry("Map/updateThreshold", 1000);
|
|
|
|
|
|
// TODO: Connect signals from the dataprovider to the qgisapp
|
|
|
|
// Do a passthrough for the status bar text
|
|
#if 0
|
|
connect(
|
|
mDataProvider, SIGNAL( statusChanged( QString ) ),
|
|
this, SLOT( showStatusMessage( QString ) )
|
|
);
|
|
#endif
|
|
QgsDebugMsg( "(8 arguments) exiting." );
|
|
|
|
emit statusChanged( tr( "QgsRasterLayer created" ) );
|
|
} // QgsRasterLayer ctor
|
|
|
|
QgsRasterLayer::~QgsRasterLayer()
|
|
{
|
|
|
|
if ( mProviderKey.isEmpty() )
|
|
{
|
|
if ( mGdalBaseDataset )
|
|
{
|
|
GDALDereferenceDataset( mGdalBaseDataset );
|
|
}
|
|
|
|
if ( mGdalDataset )
|
|
{
|
|
GDALClose( mGdalDataset );
|
|
}
|
|
}
|
|
delete mRasterShader;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////
|
|
//
|
|
// Static Methods and members
|
|
//
|
|
/////////////////////////////////////////////////////////
|
|
/**
|
|
Builds the list of file filter strings to later be used by
|
|
QgisApp::addRasterLayer()
|
|
|
|
We query GDAL for a list of supported raster formats; we then build
|
|
a list of file filter strings from that list. We return a string
|
|
that contains this list that is suitable for use in a
|
|
QFileDialog::getOpenFileNames() call.
|
|
|
|
*/
|
|
void QgsRasterLayer::buildSupportedRasterFileFilter( QString & theFileFiltersString )
|
|
{
|
|
// first get the GDAL driver manager
|
|
registerGdalDrivers();
|
|
|
|
// then iterate through all of the supported drivers, adding the
|
|
// corresponding file filter
|
|
|
|
GDALDriverH myGdalDriver; // current driver
|
|
|
|
char **myGdalDriverMetadata; // driver metadata strings
|
|
|
|
QString myGdalDriverLongName( "" ); // long name for the given driver
|
|
QString myGdalDriverExtension( "" ); // file name extension for given driver
|
|
QString myGdalDriverDescription; // QString wrapper of GDAL driver description
|
|
|
|
QStringList metadataTokens; // essentially the metadata string delimited by '='
|
|
|
|
QStringList catchallFilter; // for Any file(*.*), but also for those
|
|
// drivers with no specific file filter
|
|
|
|
GDALDriverH jp2Driver = NULL; // first JPEG2000 driver found
|
|
|
|
// Grind through all the drivers and their respective metadata.
|
|
// We'll add a file filter for those drivers that have a file
|
|
// extension defined for them; the others, well, even though
|
|
// theoreticaly we can open those files because there exists a
|
|
// driver for them, the user will have to use the "All Files" to
|
|
// open datasets with no explicitly defined file name extension.
|
|
// Note that file name extension strings are of the form
|
|
// "DMD_EXTENSION=.*". We'll also store the long name of the
|
|
// driver, which will be found in DMD_LONGNAME, which will have the
|
|
// same form.
|
|
|
|
// start with the default case
|
|
theFileFiltersString = tr( "[GDAL] All files (*)" );
|
|
|
|
for ( int i = 0; i < GDALGetDriverCount(); ++i )
|
|
{
|
|
myGdalDriver = GDALGetDriver( i );
|
|
|
|
Q_CHECK_PTR( myGdalDriver );
|
|
|
|
if ( !myGdalDriver )
|
|
{
|
|
QgsLogger::warning( "unable to get driver " + QString::number( i ) );
|
|
continue;
|
|
}
|
|
// now we need to see if the driver is for something currently
|
|
// supported; if not, we give it a miss for the next driver
|
|
|
|
myGdalDriverDescription = GDALGetDescription( myGdalDriver );
|
|
|
|
// QgsDebugMsg(QString("got driver string %1").arg(myGdalDriverDescription));
|
|
|
|
myGdalDriverMetadata = GDALGetMetadata( myGdalDriver, NULL );
|
|
|
|
// presumably we know we've run out of metadta if either the
|
|
// address is 0, or the first character is null
|
|
while ( myGdalDriverMetadata && '\0' != myGdalDriverMetadata[0] )
|
|
{
|
|
metadataTokens = QString( *myGdalDriverMetadata ).split( "=", QString::SkipEmptyParts );
|
|
// QgsDebugMsg(QString("\t%1").arg(*myGdalDriverMetadata));
|
|
|
|
// XXX add check for malformed metadataTokens
|
|
|
|
// Note that it's oddly possible for there to be a
|
|
// DMD_EXTENSION with no corresponding defined extension
|
|
// string; so we check that there're more than two tokens.
|
|
|
|
if ( metadataTokens.count() > 1 )
|
|
{
|
|
if ( "DMD_EXTENSION" == metadataTokens[0] )
|
|
{
|
|
myGdalDriverExtension = metadataTokens[1];
|
|
|
|
}
|
|
else if ( "DMD_LONGNAME" == metadataTokens[0] )
|
|
{
|
|
myGdalDriverLongName = metadataTokens[1];
|
|
|
|
// remove any superfluous (.*) strings at the end as
|
|
// they'll confuse QFileDialog::getOpenFileNames()
|
|
|
|
myGdalDriverLongName.remove( QRegExp( "\\(.*\\)$" ) );
|
|
}
|
|
}
|
|
// if we have both the file name extension and the long name,
|
|
// then we've all the information we need for the current
|
|
// driver; therefore emit a file filter string and move to
|
|
// the next driver
|
|
if ( !( myGdalDriverExtension.isEmpty() || myGdalDriverLongName.isEmpty() ) )
|
|
{
|
|
// XXX add check for SDTS; in that case we want (*CATD.DDF)
|
|
QString glob = "*." + myGdalDriverExtension.replace( "/", " *." );
|
|
// Add only the first JP2 driver found to the filter list (it's the one GDAL uses)
|
|
if ( myGdalDriverDescription == "JPEG2000" ||
|
|
myGdalDriverDescription.startsWith( "JP2" ) ) // JP2ECW, JP2KAK, JP2MrSID
|
|
{
|
|
if ( jp2Driver )
|
|
break; // skip if already found a JP2 driver
|
|
|
|
jp2Driver = myGdalDriver; // first JP2 driver found
|
|
glob += " *.j2k"; // add alternate extension
|
|
}
|
|
else if ( myGdalDriverDescription == "GTiff" )
|
|
{
|
|
glob += " *.tiff";
|
|
}
|
|
else if ( myGdalDriverDescription == "JPEG" )
|
|
{
|
|
glob += " *.jpeg";
|
|
}
|
|
|
|
theFileFiltersString += ";;[GDAL] " + myGdalDriverLongName + " (" + glob.toLower() + " " + glob.toUpper() + ")";
|
|
|
|
break; // ... to next driver, if any.
|
|
}
|
|
|
|
++myGdalDriverMetadata;
|
|
|
|
} // each metadata item
|
|
|
|
if ( myGdalDriverExtension.isEmpty() && !myGdalDriverLongName.isEmpty() )
|
|
{
|
|
// Then what we have here is a driver with no corresponding
|
|
// file extension; e.g., GRASS. In which case we append the
|
|
// string to the "catch-all" which will match all file types.
|
|
// (I.e., "*.*") We use the driver description intead of the
|
|
// long time to prevent the catch-all line from getting too
|
|
// large.
|
|
|
|
// ... OTOH, there are some drivers with missing
|
|
// DMD_EXTENSION; so let's check for them here and handle
|
|
// them appropriately
|
|
|
|
// USGS DEMs use "*.dem"
|
|
if ( myGdalDriverDescription.startsWith( "USGSDEM" ) )
|
|
{
|
|
QString glob = "*.dem";
|
|
theFileFiltersString += ";;[GDAL] " + myGdalDriverLongName + " (" + glob.toLower() + " " + glob.toUpper() + ")";
|
|
}
|
|
else if ( myGdalDriverDescription.startsWith( "DTED" ) )
|
|
{
|
|
// DTED use "*.dt0, *.dt1, *.dt2"
|
|
QString glob = "*.dt0";
|
|
glob += " *.dt1";
|
|
glob += " *.dt2";
|
|
theFileFiltersString += ";;[GDAL] " + myGdalDriverLongName + " (" + glob.toLower() + " " + glob.toUpper() + ")";
|
|
}
|
|
else if ( myGdalDriverDescription.startsWith( "MrSID" ) )
|
|
{
|
|
// MrSID use "*.sid"
|
|
QString glob = "*.sid";
|
|
theFileFiltersString += ";;[GDAL] " + myGdalDriverLongName + " (" + glob.toLower() + " " + glob.toUpper() + ")";
|
|
}
|
|
else
|
|
{
|
|
catchallFilter << QString( GDALGetDescription( myGdalDriver ) );
|
|
}
|
|
}
|
|
|
|
myGdalDriverExtension = myGdalDriverLongName = ""; // reset for next driver
|
|
|
|
} // each loaded GDAL driver
|
|
|
|
QgsDebugMsg( "Raster filter list built: " + theFileFiltersString );
|
|
} // buildSupportedRasterFileFilter_()
|
|
|
|
/**
|
|
* This helper checks to see whether the file name appears to be a valid raster file name
|
|
*/
|
|
bool QgsRasterLayer::isValidRasterFileName( QString const & theFileNameQString, QString & retErrMsg )
|
|
{
|
|
GDALDatasetH myDataset;
|
|
registerGdalDrivers();
|
|
|
|
CPLErrorReset();
|
|
|
|
//open the file using gdal making sure we have handled locale properly
|
|
myDataset = GDALOpen( TO8F( theFileNameQString ), GA_ReadOnly );
|
|
if ( myDataset == NULL )
|
|
{
|
|
if ( CPLGetLastErrorNo() != CPLE_OpenFailed )
|
|
retErrMsg = QString::fromUtf8( CPLGetLastErrorMsg() );
|
|
return false;
|
|
}
|
|
else if ( GDALGetRasterCount( myDataset ) == 0 )
|
|
{
|
|
QStringList layers = subLayers( myDataset );
|
|
if ( layers.size() == 0 )
|
|
{
|
|
GDALClose( myDataset );
|
|
myDataset = NULL;
|
|
retErrMsg = tr( "This raster file has no bands and is invalid as a raster layer." );
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
GDALClose( myDataset );
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool QgsRasterLayer::isValidRasterFileName( QString const & theFileNameQString )
|
|
{
|
|
QString retErrMsg;
|
|
return isValidRasterFileName( theFileNameQString, retErrMsg );
|
|
}
|
|
|
|
QDateTime QgsRasterLayer::lastModified( QString const & name )
|
|
{
|
|
QgsDebugMsg( "name=" + name );
|
|
QDateTime t;
|
|
|
|
QFileInfo fi( name );
|
|
|
|
// Is it file?
|
|
if ( !fi.exists() ) return t;
|
|
|
|
t = fi.lastModified();
|
|
|
|
QgsDebugMsg( "last modified = " + t.toString() );
|
|
|
|
return t;
|
|
}
|
|
|
|
|
|
|
|
void QgsRasterLayer::registerGdalDrivers()
|
|
{
|
|
if ( GDALGetDriverCount() == 0 )
|
|
GDALAllRegister();
|
|
}
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////
|
|
//
|
|
//Random Static convenience function
|
|
//
|
|
/////////////////////////////////////////////////////////
|
|
//TODO: Change these to private function or make seprate class
|
|
// convenience function for building metadata() HTML table cells
|
|
static QString makeTableCell_( QString const & value )
|
|
{
|
|
return "<p>\n" + value + "</p>\n";
|
|
} // makeTableCell_
|
|
|
|
|
|
|
|
// convenience function for building metadata() HTML table cells
|
|
static QString makeTableCells_( QStringList const & values )
|
|
{
|
|
QString s( "<tr>" );
|
|
|
|
for ( QStringList::const_iterator i = values.begin();
|
|
i != values.end();
|
|
++i )
|
|
{
|
|
s += makeTableCell_( *i );
|
|
}
|
|
|
|
s += "</tr>";
|
|
|
|
return s;
|
|
} // makeTableCell_
|
|
|
|
|
|
|
|
// convenience function for creating a string list from a C style string list
|
|
static QStringList cStringList2Q_( char ** stringList )
|
|
{
|
|
QStringList strings;
|
|
|
|
// presume null terminated string list
|
|
for ( size_t i = 0; stringList[i]; ++i )
|
|
{
|
|
strings.append( stringList[i] );
|
|
}
|
|
|
|
return strings;
|
|
|
|
} // cStringList2Q_
|
|
|
|
|
|
// typedef for the QgsDataProvider class factory
|
|
typedef QgsDataProvider * classFactoryFunction_t( const QString * );
|
|
|
|
|
|
//
|
|
// global callback function
|
|
//
|
|
int CPL_STDCALL progressCallback( double dfComplete,
|
|
const char * pszMessage,
|
|
void * pProgressArg )
|
|
{
|
|
static double dfLastComplete = -1.0;
|
|
|
|
QgsRasterLayer * mypLayer = ( QgsRasterLayer * ) pProgressArg;
|
|
|
|
if ( dfLastComplete > dfComplete )
|
|
{
|
|
if ( dfLastComplete >= 1.0 )
|
|
dfLastComplete = -1.0;
|
|
else
|
|
dfLastComplete = dfComplete;
|
|
}
|
|
|
|
if ( floor( dfLastComplete*10 ) != floor( dfComplete*10 ) )
|
|
{
|
|
int nPercent = ( int ) floor( dfComplete * 100 );
|
|
|
|
if ( nPercent == 0 && pszMessage != NULL )
|
|
{
|
|
//fprintf( stdout, "%s:", pszMessage );
|
|
}
|
|
|
|
if ( nPercent == 100 )
|
|
{
|
|
//fprintf( stdout, "%d - done.\n", (int) floor(dfComplete*100) );
|
|
mypLayer->showProgress( 100 );
|
|
}
|
|
else
|
|
{
|
|
int myProgress = ( int ) floor( dfComplete * 100 );
|
|
//fprintf( stdout, "%d.", myProgress);
|
|
mypLayer->showProgress( myProgress );
|
|
//fflush( stdout );
|
|
}
|
|
}
|
|
dfLastComplete = dfComplete;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////
|
|
//
|
|
// Non Static Public methods
|
|
//
|
|
/////////////////////////////////////////////////////////
|
|
|
|
unsigned int QgsRasterLayer::bandCount()
|
|
{
|
|
return mBandCount;
|
|
}
|
|
|
|
const QString QgsRasterLayer::bandName( int theBandNo )
|
|
{
|
|
if ( theBandNo <= mRasterStatsList.size() && theBandNo > 0 )
|
|
{
|
|
//vector starts at base 0, band counts at base1 !
|
|
return mRasterStatsList[theBandNo - 1].bandName;
|
|
}
|
|
else
|
|
{
|
|
return QString( "" );
|
|
}
|
|
}
|
|
|
|
int QgsRasterLayer::bandNumber( QString const & theBandName )
|
|
{
|
|
for ( int myIterator = 0; myIterator < mRasterStatsList.size(); ++myIterator )
|
|
{
|
|
//find out the name of this band
|
|
QgsRasterBandStats myRasterBandStats = mRasterStatsList[myIterator];
|
|
QgsDebugMsg( "myRasterBandStats.bandName: " + myRasterBandStats.bandName + " :: theBandName: "
|
|
+ theBandName );
|
|
|
|
if ( myRasterBandStats.bandName == theBandName )
|
|
{
|
|
QgsDebugMsg( "********** band " + QString::number( myRasterBandStats.bandNumber ) +
|
|
" was found in bandNumber " + theBandName );
|
|
|
|
return myRasterBandStats.bandNumber;
|
|
}
|
|
}
|
|
QgsDebugMsg( "********** no band was found in bandNumber " + theBandName );
|
|
|
|
return 0; //no band was found
|
|
}
|
|
|
|
|
|
/**
|
|
* Private method to calculate statistics for a band. Populates rasterStatsMemArray.
|
|
* Calculates:
|
|
*
|
|
* <ul>
|
|
* <li>myRasterBandStats.elementCount
|
|
* <li>myRasterBandStats.minimumValue
|
|
* <li>myRasterBandStats.maximumValue
|
|
* <li>myRasterBandStats.sum
|
|
* <li>myRasterBandStats.range
|
|
* <li>myRasterBandStats.mean
|
|
* <li>myRasterBandStats.sumOfSquares
|
|
* <li>myRasterBandStats.stdDev
|
|
* <li>myRasterBandStats.colorTable
|
|
* </ul>
|
|
*
|
|
* @sa RasterBandStats
|
|
* @note This is a cpu intensive and slow task!
|
|
*/
|
|
const QgsRasterBandStats QgsRasterLayer::bandStatistics( int theBandNo )
|
|
{
|
|
// check if we have received a valid band number
|
|
if (( GDALGetRasterCount( mGdalDataset ) < theBandNo ) && mRasterType != Palette )
|
|
{
|
|
// invalid band id, return nothing
|
|
QgsRasterBandStats myNullReturnStats;
|
|
return myNullReturnStats;
|
|
}
|
|
if ( mRasterType == Palette && ( theBandNo > 3 ) )
|
|
{
|
|
// invalid band id, return nothing
|
|
QgsRasterBandStats myNullReturnStats;
|
|
return myNullReturnStats;
|
|
}
|
|
|
|
// check if we have previously gathered stats for this band...
|
|
if ( theBandNo < 1 || theBandNo > mRasterStatsList.size() )
|
|
{
|
|
// invalid band id, return nothing
|
|
QgsRasterBandStats myNullReturnStats;
|
|
return myNullReturnStats;
|
|
}
|
|
|
|
QgsRasterBandStats myRasterBandStats = mRasterStatsList[theBandNo - 1];
|
|
myRasterBandStats.bandNumber = theBandNo;
|
|
|
|
// don't bother with this if we already have stats
|
|
if ( myRasterBandStats.statsGathered )
|
|
{
|
|
return myRasterBandStats;
|
|
}
|
|
// only print message if we are actually gathering the stats
|
|
emit statusChanged( tr( "Retrieving stats for %1" ).arg( name() ) );
|
|
qApp->processEvents();
|
|
QgsDebugMsg( "stats for band " + QString::number( theBandNo ) );
|
|
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, theBandNo );
|
|
|
|
|
|
QString myColorerpretation = GDALGetColorInterpretationName( GDALGetRasterColorInterpretation( myGdalBand ) );
|
|
|
|
// XXX this sets the element count to a sensible value; but then you ADD to
|
|
// XXX it later while iterating through all the pixels?
|
|
//myRasterBandStats.elementCount = mWidth * mHeight;
|
|
|
|
myRasterBandStats.elementCount = 0; // because we'll be counting only VALID pixels later
|
|
|
|
emit statusChanged( tr( "Calculating stats for %1" ).arg( name() ) );
|
|
//reset the main app progress bar
|
|
emit drawingProgress( 0, 0 );
|
|
|
|
// let the user know we're going to possibly be taking a while
|
|
//QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
|
|
|
|
GDALDataType myDataType = GDALGetRasterDataType( myGdalBand );
|
|
|
|
int myNXBlocks, myNYBlocks, myXBlockSize, myYBlockSize;
|
|
GDALGetBlockSize( myGdalBand, &myXBlockSize, &myYBlockSize );
|
|
|
|
myNXBlocks = ( GDALGetRasterXSize( myGdalBand ) + myXBlockSize - 1 ) / myXBlockSize;
|
|
myNYBlocks = ( GDALGetRasterYSize( myGdalBand ) + myYBlockSize - 1 ) / myYBlockSize;
|
|
|
|
void *myData = CPLMalloc( myXBlockSize * myYBlockSize * ( GDALGetDataTypeSize( myDataType ) / 8 ) );
|
|
|
|
// unfortunately we need to make two passes through the data to calculate stddev
|
|
bool myFirstIterationFlag = true;
|
|
|
|
//ifdefs below to remove compiler warning about unused vars
|
|
#ifdef QGISDEBUG
|
|
int success;
|
|
double GDALminimum = GDALGetRasterMinimum( myGdalBand, &success );
|
|
|
|
if ( ! success )
|
|
{
|
|
QgsDebugMsg( "myGdalBand->GetMinimum() failed" );
|
|
}
|
|
|
|
double GDALmaximum = GDALGetRasterMaximum( myGdalBand, &success );
|
|
|
|
if ( ! success )
|
|
{
|
|
QgsDebugMsg( "myGdalBand->GetMaximum() failed" );
|
|
}
|
|
|
|
double GDALnodata = GDALGetRasterNoDataValue( myGdalBand, &success );
|
|
|
|
if ( ! success )
|
|
{
|
|
QgsDebugMsg( "myGdalBand->GetNoDataValue() failed" );
|
|
}
|
|
|
|
QgsLogger::debug( "GDALminium: ", GDALminimum, __FILE__, __FUNCTION__, __LINE__ );
|
|
QgsLogger::debug( "GDALmaximum: ", GDALmaximum, __FILE__, __FUNCTION__, __LINE__ );
|
|
QgsLogger::debug( "GDALnodata: ", GDALnodata, __FILE__, __FUNCTION__, __LINE__ );
|
|
|
|
double GDALrange[2]; // calculated min/max, as opposed to the
|
|
// dataset provided
|
|
|
|
GDALComputeRasterMinMax( myGdalBand, 1, GDALrange );
|
|
QgsLogger::debug( "approximate computed GDALminium:", GDALrange[0], __FILE__, __FUNCTION__, __LINE__, 1 );
|
|
QgsLogger::debug( "approximate computed GDALmaximum:", GDALrange[1], __FILE__, __FUNCTION__, __LINE__, 1 );
|
|
|
|
GDALComputeRasterMinMax( myGdalBand, 0, GDALrange );
|
|
QgsLogger::debug( "exactly computed GDALminium:", GDALrange[0] );
|
|
QgsLogger::debug( "exactly computed GDALmaximum:", GDALrange[1] );
|
|
|
|
QgsDebugMsg( "starting manual stat computation" );
|
|
#endif
|
|
|
|
int myGdalBandXSize = GDALGetRasterXSize( myGdalBand );
|
|
int myGdalBandYSize = GDALGetRasterYSize( myGdalBand );
|
|
for ( int iYBlock = 0; iYBlock < myNYBlocks; iYBlock++ )
|
|
{
|
|
emit drawingProgress( iYBlock, myNYBlocks * 2 );
|
|
|
|
for ( int iXBlock = 0; iXBlock < myNXBlocks; iXBlock++ )
|
|
{
|
|
int nXValid, nYValid;
|
|
GDALReadBlock( myGdalBand, iXBlock, iYBlock, myData );
|
|
|
|
// Compute the portion of the block that is valid
|
|
// for partial edge blocks.
|
|
if (( iXBlock + 1 ) * myXBlockSize > myGdalBandXSize )
|
|
nXValid = myGdalBandXSize - iXBlock * myXBlockSize;
|
|
else
|
|
nXValid = myXBlockSize;
|
|
|
|
if (( iYBlock + 1 ) * myYBlockSize > myGdalBandYSize )
|
|
nYValid = myGdalBandYSize - iYBlock * myYBlockSize;
|
|
else
|
|
nYValid = myYBlockSize;
|
|
|
|
// Collect the histogram counts.
|
|
for ( int iY = 0; iY < nYValid; iY++ )
|
|
{
|
|
for ( int iX = 0; iX < nXValid; iX++ )
|
|
{
|
|
double myValue = readValue( myData, myDataType, iX + ( iY * myXBlockSize ) );
|
|
|
|
if ( mValidNoDataValue && ( qAbs( myValue - mNoDataValue ) <= TINY_VALUE || myValue != myValue ) )
|
|
{
|
|
continue; // NULL
|
|
}
|
|
|
|
myRasterBandStats.sum += myValue;
|
|
++myRasterBandStats.elementCount;
|
|
//only use this element if we have a non null element
|
|
if ( myFirstIterationFlag )
|
|
{
|
|
//this is the first iteration so initialise vars
|
|
myFirstIterationFlag = false;
|
|
myRasterBandStats.minimumValue = myValue;
|
|
myRasterBandStats.maximumValue = myValue;
|
|
} //end of true part for first iteration check
|
|
else
|
|
{
|
|
//this is done for all subsequent iterations
|
|
if ( myValue < myRasterBandStats.minimumValue )
|
|
{
|
|
myRasterBandStats.minimumValue = myValue;
|
|
}
|
|
if ( myValue > myRasterBandStats.maximumValue )
|
|
{
|
|
myRasterBandStats.maximumValue = myValue;
|
|
}
|
|
} //end of false part for first iteration check
|
|
}
|
|
}
|
|
} //end of column wise loop
|
|
} //end of row wise loop
|
|
|
|
|
|
//end of first pass through data now calculate the range
|
|
myRasterBandStats.range = myRasterBandStats.maximumValue - myRasterBandStats.minimumValue;
|
|
//calculate the mean
|
|
myRasterBandStats.mean = myRasterBandStats.sum / myRasterBandStats.elementCount;
|
|
|
|
//for the second pass we will get the sum of the squares / mean
|
|
for ( int iYBlock = 0; iYBlock < myNYBlocks; iYBlock++ )
|
|
{
|
|
emit drawingProgress( iYBlock + myNYBlocks, myNYBlocks * 2 );
|
|
|
|
for ( int iXBlock = 0; iXBlock < myNXBlocks; iXBlock++ )
|
|
{
|
|
int nXValid, nYValid;
|
|
|
|
GDALReadBlock( myGdalBand, iXBlock, iYBlock, myData );
|
|
|
|
// Compute the portion of the block that is valid
|
|
// for partial edge blocks.
|
|
if (( iXBlock + 1 ) * myXBlockSize > myGdalBandXSize )
|
|
nXValid = myGdalBandXSize - iXBlock * myXBlockSize;
|
|
else
|
|
nXValid = myXBlockSize;
|
|
|
|
if (( iYBlock + 1 ) * myYBlockSize > myGdalBandYSize )
|
|
nYValid = myGdalBandYSize - iYBlock * myYBlockSize;
|
|
else
|
|
nYValid = myYBlockSize;
|
|
|
|
// Collect the histogram counts.
|
|
for ( int iY = 0; iY < nYValid; iY++ )
|
|
{
|
|
for ( int iX = 0; iX < nXValid; iX++ )
|
|
{
|
|
double myValue = readValue( myData, myDataType, iX + ( iY * myXBlockSize ) );
|
|
|
|
if ( mValidNoDataValue && ( qAbs( myValue - mNoDataValue ) <= TINY_VALUE || myValue != myValue ) )
|
|
{
|
|
continue; // NULL
|
|
}
|
|
|
|
myRasterBandStats.sumOfSquares += static_cast < double >
|
|
( pow( myValue - myRasterBandStats.mean, 2 ) );
|
|
}
|
|
}
|
|
} //end of column wise loop
|
|
} //end of row wise loop
|
|
|
|
//divide result by sample size - 1 and get square root to get stdev
|
|
myRasterBandStats.stdDev = static_cast < double >( sqrt( myRasterBandStats.sumOfSquares /
|
|
( myRasterBandStats.elementCount - 1 ) ) );
|
|
|
|
#ifdef QGISDEBUG
|
|
QgsLogger::debug( "************ STATS **************", 1, __FILE__, __FUNCTION__, __LINE__ );
|
|
QgsLogger::debug( "VALID NODATA", mValidNoDataValue, 1, __FILE__, __FUNCTION__, __LINE__ );
|
|
QgsLogger::debug( "NULL", mNoDataValue, 1, __FILE__, __FUNCTION__, __LINE__ );
|
|
QgsLogger::debug( "MIN", myRasterBandStats.minimumValue, 1, __FILE__, __FUNCTION__, __LINE__ );
|
|
QgsLogger::debug( "MAX", myRasterBandStats.maximumValue, 1, __FILE__, __FUNCTION__, __LINE__ );
|
|
QgsLogger::debug( "RANGE", myRasterBandStats.range, 1, __FILE__, __FUNCTION__, __LINE__ );
|
|
QgsLogger::debug( "MEAN", myRasterBandStats.mean, 1, __FILE__, __FUNCTION__, __LINE__ );
|
|
QgsLogger::debug( "STDDEV", myRasterBandStats.stdDev, 1, __FILE__, __FUNCTION__, __LINE__ );
|
|
#endif
|
|
|
|
CPLFree( myData );
|
|
myRasterBandStats.statsGathered = true;
|
|
|
|
QgsDebugMsg( "adding stats to stats collection at position " + QString::number( theBandNo - 1 ) );
|
|
//add this band to the class stats map
|
|
mRasterStatsList[theBandNo - 1] = myRasterBandStats;
|
|
emit drawingProgress( mHeight, mHeight ); //reset progress
|
|
//QApplication::restoreOverrideCursor(); //restore the cursor
|
|
QgsDebugMsg( "Stats collection completed returning" );
|
|
return myRasterBandStats;
|
|
|
|
} // QgsRasterLayer::bandStatistics
|
|
|
|
const QgsRasterBandStats QgsRasterLayer::bandStatistics( QString const & theBandName )
|
|
{
|
|
|
|
//we cant use a vector iterator because the iterator is astruct not a class
|
|
//and the qvector model does not like this.
|
|
for ( int i = 1; i <= GDALGetRasterCount( mGdalDataset ); i++ )
|
|
{
|
|
QgsRasterBandStats myRasterBandStats = bandStatistics( i );
|
|
if ( myRasterBandStats.bandName == theBandName )
|
|
{
|
|
return myRasterBandStats;
|
|
}
|
|
}
|
|
|
|
return QgsRasterBandStats(); // return a null one
|
|
}
|
|
|
|
|
|
/*
|
|
* This will speed up performance at the expense of hard drive space.
|
|
* Also, write access to the file is required for creating internal pyramids,
|
|
* and to the directory in which the files exists if external
|
|
* pyramids (.ovr) are to be created. If no parameter is passed in
|
|
* it will default to nearest neighbor resampling.
|
|
*
|
|
* @param theTryInternalFlag - Try to make the pyramids internal if supported (e.g. geotiff). If not supported it will revert to creating external .ovr file anyway.
|
|
* @return null string on success, otherwise a string specifying error
|
|
*/
|
|
QString QgsRasterLayer::buildPyramids( RasterPyramidList const & theRasterPyramidList,
|
|
QString const & theResamplingMethod, bool theTryInternalFlag )
|
|
{
|
|
//TODO: Consider making theRasterPyramidList modifyable by this method to indicate if the pyramid exists after build attempt
|
|
//without requiring the user to rebuild the pyramid list to get the updated infomation
|
|
|
|
//
|
|
// Note: Make sure the raster is not opened in write mode
|
|
// in order to force overviews to be written to a separate file.
|
|
// Otherwise reoopen it in read/write mode to stick overviews
|
|
// into the same file (if supported)
|
|
//
|
|
|
|
|
|
emit drawingProgress( 0, 0 );
|
|
//first test if the file is writeable
|
|
QFileInfo myQFile( mDataSource );
|
|
|
|
if ( !myQFile.isWritable() )
|
|
{
|
|
return "ERROR_WRITE_ACCESS";
|
|
}
|
|
|
|
if ( mGdalDataset != mGdalBaseDataset )
|
|
{
|
|
QgsLogger::warning( "Pyramid building not currently supported for 'warped virtual dataset'." );
|
|
return "ERROR_VIRTUAL";
|
|
}
|
|
|
|
if ( theTryInternalFlag )
|
|
{
|
|
// libtiff < 4.0 has a bug that prevents safe building of overviews on JPEG compressed files
|
|
// we detect libtiff < 4.0 by checking that BIGTIFF is not in the creation options of the GTiff driver
|
|
// see https://trac.osgeo.org/qgis/ticket/1357
|
|
const char* pszGTiffCreationOptions =
|
|
GDALGetMetadataItem( GDALGetDriverByName( "GTiff" ), GDAL_DMD_CREATIONOPTIONLIST, "" );
|
|
if ( strstr( pszGTiffCreationOptions, "BIGTIFF" ) == NULL )
|
|
{
|
|
QString myCompressionType = QString( GDALGetMetadataItem( mGdalDataset, "COMPRESSION", "IMAGE_STRUCTURE" ) );
|
|
if ( "JPEG" == myCompressionType )
|
|
{
|
|
return "ERROR_JPEG_COMPRESSION";
|
|
}
|
|
}
|
|
|
|
//close the gdal dataset and reopen it in read / write mode
|
|
GDALClose( mGdalDataset );
|
|
mGdalBaseDataset = GDALOpen( TO8F( mDataSource ), GA_Update );
|
|
|
|
// if the dataset couldn't be opened in read / write mode, tell the user
|
|
if ( !mGdalBaseDataset )
|
|
{
|
|
mGdalBaseDataset = GDALOpen( TO8F( mDataSource ), GA_ReadOnly );
|
|
//Since we are not a virtual warped dataset, mGdalDataSet and mGdalBaseDataset are supposed to be the same
|
|
mGdalDataset = mGdalBaseDataset;
|
|
return "ERROR_WRITE_FORMAT";
|
|
}
|
|
}
|
|
|
|
//
|
|
// Iterate through the Raster Layer Pyramid Vector, building any pyramid
|
|
// marked as exists in eaxh RasterPyramid struct.
|
|
//
|
|
CPLErr myError; //in case anything fails
|
|
int myCount = 1;
|
|
int myTotal = theRasterPyramidList.count();
|
|
RasterPyramidList::const_iterator myRasterPyramidIterator;
|
|
for ( myRasterPyramidIterator = theRasterPyramidList.begin();
|
|
myRasterPyramidIterator != theRasterPyramidList.end();
|
|
++myRasterPyramidIterator )
|
|
{
|
|
#ifdef QGISDEBUG
|
|
QgsLogger::debug( "Build pyramids:: Level", ( *myRasterPyramidIterator ).level, 1, __FILE__, __FUNCTION__, __LINE__ );
|
|
QgsLogger::debug( "x", ( *myRasterPyramidIterator ).xDim, 1, __FILE__, __FUNCTION__, __LINE__ );
|
|
QgsLogger::debug( "y", ( *myRasterPyramidIterator ).yDim, 1, __FILE__, __FUNCTION__, __LINE__ );
|
|
QgsLogger::debug( "exists :", ( *myRasterPyramidIterator ).exists, 1, __FILE__, __FUNCTION__, __LINE__ );
|
|
#endif
|
|
if (( *myRasterPyramidIterator ).build )
|
|
{
|
|
QgsDebugMsg( "Building....." );
|
|
emit drawingProgress( myCount, myTotal );
|
|
int myOverviewLevelsArray[1] = {( *myRasterPyramidIterator ).level };
|
|
/* From : http://remotesensing.org/gdal/classGDALDataset.html#a23
|
|
* pszResampling : one of "NEAREST", "AVERAGE" or "MODE" controlling the downsampling method applied.
|
|
* nOverviews : number of overviews to build.
|
|
* panOverviewList : the list of overview decimation factors to build.
|
|
* nBand : number of bands to build overviews for in panBandList. Build for all bands if this is 0.
|
|
* panBandList : list of band numbers.
|
|
* pfnProgress : a function to call to report progress, or NULL.
|
|
* pProgressData : application data to pass to the progress function.
|
|
*/
|
|
//build the pyramid and show progress to console
|
|
try
|
|
{
|
|
|
|
//build the pyramid and show progress to console
|
|
//NOTE this (magphase) is disabled in the gui since it tends
|
|
//to create corrupted images. The images can be repaired
|
|
//by running one of the other resampling strategies below.
|
|
//see ticket #284
|
|
if ( theResamplingMethod == tr( "Average Magphase" ) )
|
|
{
|
|
myError = GDALBuildOverviews( mGdalBaseDataset, "MODE", 1, myOverviewLevelsArray, 0, NULL,
|
|
progressCallback, this ); //this is the arg for the gdal progress callback
|
|
}
|
|
else if ( theResamplingMethod == tr( "Average" ) )
|
|
|
|
{
|
|
myError = GDALBuildOverviews( mGdalBaseDataset, "AVERAGE", 1, myOverviewLevelsArray, 0, NULL,
|
|
progressCallback, this ); //this is the arg for the gdal progress callback
|
|
}
|
|
else // fall back to nearest neighbor
|
|
{
|
|
myError = GDALBuildOverviews( mGdalBaseDataset, "NEAREST", 1, myOverviewLevelsArray, 0, NULL,
|
|
progressCallback, this ); //this is the arg for the gdal progress callback
|
|
}
|
|
|
|
if ( myError == CE_Failure || CPLGetLastErrorNo() == CPLE_NotSupported )
|
|
{
|
|
//something bad happenend
|
|
//QString myString = QString (CPLGetLastError());
|
|
GDALClose( mGdalBaseDataset );
|
|
mGdalBaseDataset = GDALOpen( TO8F( mDataSource ), GA_ReadOnly );
|
|
//Since we are not a virtual warped dataset, mGdalDataSet and mGdalBaseDataset are supposed to be the same
|
|
mGdalDataset = mGdalBaseDataset;
|
|
|
|
emit drawingProgress( 0, 0 );
|
|
return "FAILED_NOT_SUPPORTED";
|
|
}
|
|
else
|
|
{
|
|
//make sure the raster knows it has pyramids
|
|
mHasPyramids = true;
|
|
}
|
|
myCount++;
|
|
|
|
}
|
|
catch ( CPLErr )
|
|
{
|
|
QgsLogger::warning( "Pyramid overview building failed!" );
|
|
}
|
|
}
|
|
}
|
|
|
|
QgsDebugMsg( "Pyramid overviews built" );
|
|
if ( theTryInternalFlag )
|
|
{
|
|
//close the gdal dataset and reopen it in read only mode
|
|
GDALClose( mGdalBaseDataset );
|
|
mGdalBaseDataset = GDALOpen( TO8F( mDataSource ), GA_ReadOnly );
|
|
//Since we are not a virtual warped dataset, mGdalDataSet and mGdalBaseDataset are supposed to be the same
|
|
mGdalDataset = mGdalBaseDataset;
|
|
}
|
|
|
|
emit drawingProgress( 0, 0 );
|
|
return NULL; // returning null on success
|
|
}
|
|
|
|
|
|
QgsRasterLayer::RasterPyramidList QgsRasterLayer::buildPyramidList()
|
|
{
|
|
//
|
|
// First we build up a list of potential pyramid layers
|
|
//
|
|
int myWidth = mWidth;
|
|
int myHeight = mHeight;
|
|
int myDivisor = 2;
|
|
|
|
if ( mDataProvider ) return mPyramidList;
|
|
|
|
GDALRasterBandH myGDALBand = GDALGetRasterBand( mGdalDataset, 1 ); //just use the first band
|
|
|
|
mPyramidList.clear();
|
|
QgsDebugMsg( "Building initial pyramid list" );
|
|
while (( myWidth / myDivisor > 32 ) && (( myHeight / myDivisor ) > 32 ) )
|
|
{
|
|
|
|
QgsRasterPyramid myRasterPyramid;
|
|
myRasterPyramid.level = myDivisor;
|
|
myRasterPyramid.xDim = ( int )( 0.5 + ( myWidth / ( double )myDivisor ) );
|
|
myRasterPyramid.yDim = ( int )( 0.5 + ( myHeight / ( double )myDivisor ) );
|
|
myRasterPyramid.exists = false;
|
|
#ifdef QGISDEBUG
|
|
QgsLogger::debug( "Pyramid", myRasterPyramid.level, 1, __FILE__, __FUNCTION__, __LINE__ );
|
|
QgsLogger::debug( "xDim", myRasterPyramid.xDim, 1, __FILE__, __FUNCTION__, __LINE__ );
|
|
QgsLogger::debug( "yDim", myRasterPyramid.yDim, 1, __FILE__, __FUNCTION__, __LINE__ );
|
|
#endif
|
|
|
|
//
|
|
// Now we check if it actually exists in the raster layer
|
|
// and also adjust the dimensions if the dimensions calculated
|
|
// above are only a near match.
|
|
//
|
|
const int myNearMatchLimit = 5;
|
|
if ( GDALGetOverviewCount( myGDALBand ) > 0 )
|
|
{
|
|
int myOverviewCount;
|
|
for ( myOverviewCount = 0;
|
|
myOverviewCount < GDALGetOverviewCount( myGDALBand );
|
|
++myOverviewCount )
|
|
{
|
|
GDALRasterBandH myOverview;
|
|
myOverview = GDALGetOverview( myGDALBand, myOverviewCount );
|
|
int myOverviewXDim = GDALGetRasterBandXSize( myOverview );
|
|
int myOverviewYDim = GDALGetRasterBandYSize( myOverview );
|
|
//
|
|
// here is where we check if its a near match:
|
|
// we will see if its within 5 cells either side of
|
|
//
|
|
QgsDebugMsg( "Checking whether " + QString::number( myRasterPyramid.xDim ) + " x " +
|
|
QString::number( myRasterPyramid.yDim ) + " matches " +
|
|
QString::number( myOverviewXDim ) + " x " + QString::number( myOverviewYDim ) );
|
|
|
|
|
|
if (( myOverviewXDim <= ( myRasterPyramid.xDim + myNearMatchLimit ) ) &&
|
|
( myOverviewXDim >= ( myRasterPyramid.xDim - myNearMatchLimit ) ) &&
|
|
( myOverviewYDim <= ( myRasterPyramid.yDim + myNearMatchLimit ) ) &&
|
|
( myOverviewYDim >= ( myRasterPyramid.yDim - myNearMatchLimit ) ) )
|
|
{
|
|
//right we have a match so adjust the a / y before they get added to the list
|
|
myRasterPyramid.xDim = myOverviewXDim;
|
|
myRasterPyramid.yDim = myOverviewYDim;
|
|
myRasterPyramid.exists = true;
|
|
QgsDebugMsg( ".....YES!" );
|
|
}
|
|
else
|
|
{
|
|
//no match
|
|
QgsDebugMsg( ".....no." );
|
|
}
|
|
}
|
|
}
|
|
mPyramidList.append( myRasterPyramid );
|
|
//sqare the divisor each step
|
|
myDivisor = ( myDivisor * 2 );
|
|
}
|
|
|
|
return mPyramidList;
|
|
}
|
|
|
|
QString QgsRasterLayer::colorShadingAlgorithmAsString() const
|
|
{
|
|
switch ( mColorShadingAlgorithm )
|
|
{
|
|
case PseudoColorShader:
|
|
return QString( "PseudoColorShader" );
|
|
break;
|
|
case FreakOutShader:
|
|
return QString( "FreakOutShader" );
|
|
break;
|
|
case ColorRampShader:
|
|
return QString( "ColorRampShader" );
|
|
break;
|
|
case UserDefinedShader:
|
|
return QString( "UserDefinedShader" );
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return QString( "UndefinedShader" );
|
|
}
|
|
|
|
/**
|
|
* @param theBand The band (number) for which to estimate the min max values
|
|
* @param theMinMax Pointer to a double[2] which hold the estimated min max
|
|
*/
|
|
void QgsRasterLayer::computeMinimumMaximumEstimates( int theBand, double* theMinMax )
|
|
{
|
|
if ( 0 == theMinMax ) { return; }
|
|
|
|
if ( 0 < theBand && theBand <= ( int ) bandCount() )
|
|
{
|
|
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, theBand );
|
|
GDALComputeRasterMinMax( myGdalBand, 1, theMinMax );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param theBand The band (name) for which to estimate the min max values
|
|
* @param theMinMax Pointer to a double[2] which hold the estimated min max
|
|
*/
|
|
void QgsRasterLayer::computeMinimumMaximumEstimates( QString theBand, double* theMinMax )
|
|
{
|
|
computeMinimumMaximumEstimates( bandNumber( theBand ), theMinMax );
|
|
}
|
|
|
|
void QgsRasterLayer::computeMinimumMaximumEstimates( int theBand, double& theMin, double& theMax )
|
|
{
|
|
double theMinMax[2];
|
|
computeMinimumMaximumEstimates( theBand, theMinMax );
|
|
theMin = theMinMax[0];
|
|
theMax = theMinMax[1];
|
|
}
|
|
|
|
/**
|
|
* @param theBand The band (number) for which to calculate the min max values
|
|
* @param theMinMax Pointer to a double[2] which hold the estimated min max
|
|
*/
|
|
void QgsRasterLayer::computeMinimumMaximumFromLastExtent( int theBand, double* theMinMax )
|
|
{
|
|
if ( 0 == theMinMax ) { return; }
|
|
|
|
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, theBand );
|
|
GDALDataType myDataType = GDALGetRasterDataType( myGdalBand );
|
|
void* myGdalScanData = readData( myGdalBand, &mLastViewPort );
|
|
|
|
/* Check for out of memory error */
|
|
if ( myGdalScanData == NULL )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( 0 < theBand && theBand <= ( int ) bandCount() )
|
|
{
|
|
float myMin = std::numeric_limits<float>::max();
|
|
float myMax = -1 * std::numeric_limits<float>::max();
|
|
float myValue = 0.0;
|
|
for ( int myRow = 0; myRow < mLastViewPort.drawableAreaYDim; ++myRow )
|
|
{
|
|
for ( int myColumn = 0; myColumn < mLastViewPort.drawableAreaXDim; ++myColumn )
|
|
{
|
|
myValue = readValue( myGdalScanData, myDataType, myRow * mLastViewPort.drawableAreaXDim + myColumn );
|
|
if ( mValidNoDataValue && ( qAbs( myValue - mNoDataValue ) <= TINY_VALUE || myValue != myValue ) )
|
|
{
|
|
continue;
|
|
}
|
|
myMin = qMin( myMin, myValue );
|
|
myMax = qMax( myMax, myValue );
|
|
}
|
|
}
|
|
theMinMax[0] = myMin;
|
|
theMinMax[1] = myMax;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param theBand The band (name) for which to calculate the min max values
|
|
* @param theMinMax Pointer to a double[2] which hold the estimated min max
|
|
*/
|
|
void QgsRasterLayer::computeMinimumMaximumFromLastExtent( QString theBand, double* theMinMax )
|
|
{
|
|
computeMinimumMaximumFromLastExtent( bandNumber( theBand ), theMinMax );
|
|
}
|
|
|
|
void QgsRasterLayer::computeMinimumMaximumFromLastExtent( int theBand, double& theMin, double& theMax )
|
|
{
|
|
double theMinMax[2];
|
|
computeMinimumMaximumFromLastExtent( theBand, theMinMax );
|
|
theMin = theMinMax[0];
|
|
theMax = theMinMax[1];
|
|
}
|
|
|
|
/**
|
|
* @param theBand The band (number) for which to get the contrast enhancement for
|
|
* @return Pointer to the contrast enhancement or 0 on failure
|
|
*/
|
|
QgsContrastEnhancement* QgsRasterLayer::contrastEnhancement( unsigned int theBand )
|
|
{
|
|
if ( 0 < theBand && theBand <= bandCount() )
|
|
{
|
|
return &mContrastEnhancementList[theBand - 1];
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
QString QgsRasterLayer::contrastEnhancementAlgorithmAsString() const
|
|
{
|
|
switch ( mContrastEnhancementAlgorithm )
|
|
{
|
|
case QgsContrastEnhancement::NoEnhancement:
|
|
return QString( "NoEnhancement" );
|
|
break;
|
|
case QgsContrastEnhancement::StretchToMinimumMaximum:
|
|
return QString( "StretchToMinimumMaximum" );
|
|
break;
|
|
case QgsContrastEnhancement::StretchAndClipToMinimumMaximum:
|
|
return QString( "StretchAndClipToMinimumMaximum" );
|
|
break;
|
|
case QgsContrastEnhancement::ClipToMinimumMaximum:
|
|
return QString( "ClipToMinimumMaximum" );
|
|
break;
|
|
case QgsContrastEnhancement::UserDefinedEnhancement:
|
|
return QString( "UserDefined" );
|
|
break;
|
|
}
|
|
|
|
return QString( "NoEnhancement" );
|
|
}
|
|
|
|
/**
|
|
* @note Note implemented yet
|
|
* @return always returns false
|
|
*/
|
|
bool QgsRasterLayer::copySymbologySettings( const QgsMapLayer& theOther )
|
|
{
|
|
//preventwarnings
|
|
if ( theOther.type() < 0 )
|
|
{
|
|
return false;
|
|
}
|
|
return false;
|
|
} //todo
|
|
|
|
/**
|
|
* @param theBandNo the band number
|
|
* @return pointer to the color table
|
|
*/
|
|
QList<QgsColorRampShader::ColorRampItem>* QgsRasterLayer::colorTable( int theBandNo )
|
|
{
|
|
return &( mRasterStatsList[theBandNo-1].colorTable );
|
|
}
|
|
|
|
/**
|
|
* @return 0 if not using the data provider model (i.e. directly using GDAL)
|
|
*/
|
|
QgsRasterDataProvider* QgsRasterLayer::dataProvider()
|
|
{
|
|
return mDataProvider;
|
|
}
|
|
|
|
/**
|
|
* @return 0 if not using the data provider model (i.e. directly using GDAL)
|
|
*/
|
|
const QgsRasterDataProvider* QgsRasterLayer::dataProvider() const
|
|
{
|
|
return mDataProvider;
|
|
}
|
|
|
|
void QgsRasterLayer::reload()
|
|
{
|
|
if ( mDataProvider )
|
|
{
|
|
mDataProvider->reloadData();
|
|
}
|
|
}
|
|
|
|
bool QgsRasterLayer::draw( QgsRenderContext& rendererContext )
|
|
{
|
|
QgsDebugMsg( "entered. (renderContext)" );
|
|
|
|
// Don't waste time drawing if transparency is at 0 (completely transparent)
|
|
if ( mTransparencyLevel == 0 )
|
|
return true;
|
|
|
|
QgsDebugMsg( "checking timestamp." );
|
|
|
|
// Check timestamp
|
|
if ( !update() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const QgsMapToPixel& theQgsMapToPixel = rendererContext.mapToPixel();
|
|
const QgsRectangle& theViewExtent = rendererContext.extent();
|
|
QPainter* theQPainter = rendererContext.painter();
|
|
|
|
if ( !theQPainter )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// clip raster extent to view extent
|
|
QgsRectangle myRasterExtent = theViewExtent.intersect( &mLayerExtent );
|
|
if ( myRasterExtent.isEmpty() )
|
|
{
|
|
QgsDebugMsg( "draw request outside view extent." );
|
|
// nothing to do
|
|
return true;
|
|
}
|
|
|
|
QgsDebugMsg( "theViewExtent is " + theViewExtent.toString() );
|
|
QgsDebugMsg( "myRasterExtent is " + myRasterExtent.toString() );
|
|
|
|
//
|
|
// The first thing we do is set up the QgsRasterViewPort. This struct stores all the settings
|
|
// relating to the size (in pixels and coordinate system units) of the raster part that is
|
|
// in view in the map window. It also stores the origin.
|
|
//
|
|
//this is not a class level member because every time the user pans or zooms
|
|
//the contents of the rasterViewPort will change
|
|
QgsRasterViewPort *myRasterViewPort = new QgsRasterViewPort();
|
|
|
|
|
|
// calculate raster pixel offsets from origin to clipped rect
|
|
// we're only interested in positive offsets where the origin of the raster
|
|
// is northwest of the origin of the view
|
|
myRasterViewPort->rectXOffsetFloat = ( theViewExtent.xMinimum() - mLayerExtent.xMinimum() ) / qAbs( mGeoTransform[1] );
|
|
myRasterViewPort->rectYOffsetFloat = ( mLayerExtent.yMaximum() - theViewExtent.yMaximum() ) / qAbs( mGeoTransform[5] );
|
|
|
|
if ( myRasterViewPort->rectXOffsetFloat < 0 )
|
|
{
|
|
myRasterViewPort->rectXOffsetFloat = 0;
|
|
}
|
|
|
|
if ( myRasterViewPort->rectYOffsetFloat < 0 )
|
|
{
|
|
myRasterViewPort->rectYOffsetFloat = 0;
|
|
}
|
|
|
|
myRasterViewPort->rectXOffset = static_cast < int >( myRasterViewPort->rectXOffsetFloat );
|
|
myRasterViewPort->rectYOffset = static_cast < int >( myRasterViewPort->rectYOffsetFloat );
|
|
|
|
QgsDebugMsgLevel( QString( "mGeoTransform: %1, %2, %3, %4, %5, %6" )
|
|
.arg( mGeoTransform[0] )
|
|
.arg( mGeoTransform[1] )
|
|
.arg( mGeoTransform[2] )
|
|
.arg( mGeoTransform[3] )
|
|
.arg( mGeoTransform[4] )
|
|
.arg( mGeoTransform[5] ), 3 );
|
|
|
|
// get dimensions of clipped raster image in raster pixel space/ RasterIO will do the scaling for us.
|
|
// So for example, if the user is zoomed in a long way, there may only be e.g. 5x5 pixels retrieved from
|
|
// the raw raster data, but rasterio will seamlessly scale the up to whatever the screen coordinats are
|
|
// e.g. a 600x800 display window (see next section below)
|
|
myRasterViewPort->clippedXMin = ( myRasterExtent.xMinimum() - mGeoTransform[0] ) / mGeoTransform[1];
|
|
myRasterViewPort->clippedXMax = ( myRasterExtent.xMaximum() - mGeoTransform[0] ) / mGeoTransform[1];
|
|
myRasterViewPort->clippedYMin = ( myRasterExtent.yMinimum() - mGeoTransform[3] ) / mGeoTransform[5];
|
|
myRasterViewPort->clippedYMax = ( myRasterExtent.yMaximum() - mGeoTransform[3] ) / mGeoTransform[5];
|
|
|
|
// Sometimes the Ymin/Ymax are reversed.
|
|
if ( myRasterViewPort->clippedYMin > myRasterViewPort->clippedYMax )
|
|
{
|
|
double t = myRasterViewPort->clippedYMin;
|
|
myRasterViewPort->clippedYMin = myRasterViewPort->clippedYMax;
|
|
myRasterViewPort->clippedYMax = t;
|
|
}
|
|
|
|
// Set the clipped width and height to encompass all of the source pixels
|
|
// that could end up being displayed.
|
|
myRasterViewPort->clippedWidth =
|
|
static_cast<int>( ceil( myRasterViewPort->clippedXMax ) - floor( myRasterViewPort->clippedXMin ) );
|
|
|
|
myRasterViewPort->clippedHeight =
|
|
static_cast<int>( ceil( myRasterViewPort->clippedYMax ) - floor( myRasterViewPort->clippedYMin ) );
|
|
|
|
// but make sure the intended SE corner extent doesn't exceed the SE corner
|
|
// of the source raster, otherwise GDAL's RasterIO gives an error and returns nothing.
|
|
// The SE corner = NW origin + dimensions of the image itself.
|
|
if (( myRasterViewPort->rectXOffset + myRasterViewPort->clippedWidth )
|
|
> mWidth )
|
|
{
|
|
myRasterViewPort->clippedWidth =
|
|
mWidth - myRasterViewPort->rectXOffset;
|
|
}
|
|
if (( myRasterViewPort->rectYOffset + myRasterViewPort->clippedHeight )
|
|
> mHeight )
|
|
{
|
|
myRasterViewPort->clippedHeight =
|
|
mHeight - myRasterViewPort->rectYOffset;
|
|
}
|
|
|
|
// get dimensions of clipped raster image in device coordinate space (this is the size of the viewport)
|
|
myRasterViewPort->topLeftPoint = theQgsMapToPixel.transform( myRasterExtent.xMinimum(), myRasterExtent.yMaximum() );
|
|
myRasterViewPort->bottomRightPoint = theQgsMapToPixel.transform( myRasterExtent.xMaximum(), myRasterExtent.yMinimum() );
|
|
|
|
myRasterViewPort->drawableAreaXDim = static_cast<int>( qAbs(( myRasterViewPort->clippedWidth / theQgsMapToPixel.mapUnitsPerPixel() * mGeoTransform[1] ) ) + 0.5 );
|
|
myRasterViewPort->drawableAreaYDim = static_cast<int>( qAbs(( myRasterViewPort->clippedHeight / theQgsMapToPixel.mapUnitsPerPixel() * mGeoTransform[5] ) ) + 0.5 );
|
|
|
|
//the drawable area can start to get very very large when you get down displaying 2x2 or smaller, this is becasue
|
|
//theQgsMapToPixel.mapUnitsPerPixel() is less then 1,
|
|
//so we will just get the pixel data and then render these special cases differently in paintImageToCanvas()
|
|
if ( 2 >= myRasterViewPort->clippedWidth && 2 >= myRasterViewPort->clippedHeight )
|
|
{
|
|
myRasterViewPort->drawableAreaXDim = myRasterViewPort->clippedWidth;
|
|
myRasterViewPort->drawableAreaYDim = myRasterViewPort->clippedHeight;
|
|
}
|
|
|
|
QgsDebugMsgLevel( QString( "mapUnitsPerPixel = %1" ).arg( theQgsMapToPixel.mapUnitsPerPixel() ), 3 );
|
|
QgsDebugMsgLevel( QString( "mWidth = %1" ).arg( mWidth ), 3 );
|
|
QgsDebugMsgLevel( QString( "mHeight = %1" ).arg( mHeight ), 3 );
|
|
QgsDebugMsgLevel( QString( "rectXOffset = %1" ).arg( myRasterViewPort->rectXOffset ), 3 );
|
|
QgsDebugMsgLevel( QString( "rectXOffsetFloat = %1" ).arg( myRasterViewPort->rectXOffsetFloat ), 3 );
|
|
QgsDebugMsgLevel( QString( "rectYOffset = %1" ).arg( myRasterViewPort->rectYOffset ), 3 );
|
|
QgsDebugMsgLevel( QString( "rectYOffsetFloat = %1" ).arg( myRasterViewPort->rectYOffsetFloat ), 3 );
|
|
|
|
QgsDebugMsgLevel( QString( "myRasterExtent.xMinimum() = %1" ).arg( myRasterExtent.xMinimum() ), 3 );
|
|
QgsDebugMsgLevel( QString( "myRasterExtent.xMaximum() = %1" ).arg( myRasterExtent.xMaximum() ), 3 );
|
|
QgsDebugMsgLevel( QString( "myRasterExtent.yMinimum() = %1" ).arg( myRasterExtent.yMinimum() ), 3 );
|
|
QgsDebugMsgLevel( QString( "myRasterExtent.yMaximum() = %1" ).arg( myRasterExtent.yMaximum() ), 3 );
|
|
|
|
QgsDebugMsgLevel( QString( "topLeftPoint.x() = %1" ).arg( myRasterViewPort->topLeftPoint.x() ), 3 );
|
|
QgsDebugMsgLevel( QString( "bottomRightPoint.x() = %1" ).arg( myRasterViewPort->bottomRightPoint.x() ), 3 );
|
|
QgsDebugMsgLevel( QString( "topLeftPoint.y() = %1" ).arg( myRasterViewPort->topLeftPoint.y() ), 3 );
|
|
QgsDebugMsgLevel( QString( "bottomRightPoint.y() = %1" ).arg( myRasterViewPort->bottomRightPoint.y() ), 3 );
|
|
|
|
QgsDebugMsgLevel( QString( "clippedXMin = %1" ).arg( myRasterViewPort->clippedXMin ), 3 );
|
|
QgsDebugMsgLevel( QString( "clippedXMax = %1" ).arg( myRasterViewPort->clippedXMax ), 3 );
|
|
QgsDebugMsgLevel( QString( "clippedYMin = %1" ).arg( myRasterViewPort->clippedYMin ), 3 );
|
|
QgsDebugMsgLevel( QString( "clippedYMax = %1" ).arg( myRasterViewPort->clippedYMax ), 3 );
|
|
|
|
QgsDebugMsgLevel( QString( "clippedWidth = %1" ).arg( myRasterViewPort->clippedWidth ), 3 );
|
|
QgsDebugMsgLevel( QString( "clippedHeight = %1" ).arg( myRasterViewPort->clippedHeight ), 3 );
|
|
QgsDebugMsgLevel( QString( "drawableAreaXDim = %1" ).arg( myRasterViewPort->drawableAreaXDim ), 3 );
|
|
QgsDebugMsgLevel( QString( "drawableAreaYDim = %1" ).arg( myRasterViewPort->drawableAreaYDim ), 3 );
|
|
|
|
QgsDebugMsgLevel( "ReadXml: gray band name : " + mGrayBandName, 3 );
|
|
QgsDebugMsgLevel( "ReadXml: red band name : " + mRedBandName, 3 );
|
|
QgsDebugMsgLevel( "ReadXml: green band name : " + mGreenBandName, 3 );
|
|
QgsDebugMsgLevel( "ReadXml: blue band name : " + mBlueBandName, 3 );
|
|
|
|
// /\/\/\ - added to handle zoomed-in rasters
|
|
|
|
mLastViewPort = *myRasterViewPort;
|
|
|
|
// Provider mode: See if a provider key is specified, and if so use the provider instead
|
|
|
|
QgsDebugMsg( "Checking for provider key." );
|
|
|
|
if ( !mProviderKey.isEmpty() )
|
|
{
|
|
QgsDebugMsg( "Wanting a '" + mProviderKey + "' provider to draw this." );
|
|
|
|
mDataProvider->setDpi( rendererContext.rasterScaleFactor() * 25.4 * rendererContext.scaleFactor() );
|
|
|
|
//fetch image in several parts if it is too memory consuming
|
|
//also some WMS servers have a pixel limit, so it's better to make several requests
|
|
int totalPixelWidth = qAbs(( myRasterViewPort->clippedXMax - myRasterViewPort->clippedXMin )
|
|
/ theQgsMapToPixel.mapUnitsPerPixel() * mGeoTransform[1] ) + 1;
|
|
int totalPixelHeight = qAbs(( myRasterViewPort->clippedYMax - myRasterViewPort->clippedYMin )
|
|
/ theQgsMapToPixel.mapUnitsPerPixel() * mGeoTransform[5] ) + 1;
|
|
int numParts = totalPixelWidth * totalPixelHeight / 5000000 + 1.0;
|
|
int numRowsPerPart = totalPixelHeight / numParts + 1.0;
|
|
|
|
|
|
int currentPixelOffsetY = 0; //top y-coordinate of current raster part
|
|
//the width of a WMS image part
|
|
int pixelWidth = ( myRasterExtent.xMaximum() - myRasterExtent.xMinimum() ) / theQgsMapToPixel.mapUnitsPerPixel() + 0.5;
|
|
for ( int i = 0; i < numParts; ++i )
|
|
{
|
|
//fetch a small overlap of 2 pixels between two adjacent tiles to avoid white stripes
|
|
QgsRectangle rasterPartRect( myRasterExtent.xMinimum(), myRasterExtent.yMaximum() - ( currentPixelOffsetY + numRowsPerPart + 2 ) * theQgsMapToPixel.mapUnitsPerPixel(),
|
|
myRasterExtent.xMaximum(), myRasterExtent.yMaximum() - currentPixelOffsetY * theQgsMapToPixel.mapUnitsPerPixel() );
|
|
|
|
int pixelHeight = rasterPartRect.height() / theQgsMapToPixel.mapUnitsPerPixel() + 0.5;
|
|
|
|
/*
|
|
QgsDebugMsg( "**********WMS tile parameter***************" );
|
|
QgsDebugMsg( "pixelWidth: " + QString::number( pixelWidth ) );
|
|
QgsDebugMsg( "pixelHeight: " + QString::number( pixelHeight ) );
|
|
QgsDebugMsg( "mapWidth: " + QString::number( rasterPartRect.width() ) );
|
|
QgsDebugMsg( "mapHeight: " + QString::number( rasterPartRect.height(), 'f', 8 ) );
|
|
QgsDebugMsg( "mapUnitsPerPixel: " + QString::number( theQgsMapToPixel.mapUnitsPerPixel() ) );*/
|
|
|
|
QImage* image = mDataProvider->draw( rasterPartRect, pixelWidth, pixelHeight );
|
|
|
|
if ( !image )
|
|
{
|
|
// An error occurred.
|
|
mErrorCaption = mDataProvider->lastErrorTitle();
|
|
mError = mDataProvider->lastError();
|
|
|
|
delete myRasterViewPort;
|
|
return false;
|
|
}
|
|
|
|
QgsDebugMsg( "done mDataProvider->draw." );
|
|
|
|
QgsDebugMsgLevel( QString( "image stats: depth=%1 bytes=%2 width=%3 height=%4" ).arg( image->depth() )
|
|
.arg( image->numBytes() )
|
|
.arg( image->width() )
|
|
.arg( image->height() ),
|
|
3 );
|
|
|
|
QgsDebugMsgLevel( QString( "Want to theQPainter->drawImage with origin x: %1 (%2) %3 (%4)" )
|
|
.arg( myRasterViewPort->topLeftPoint.x() ).arg( static_cast<int>( myRasterViewPort->topLeftPoint.x() ) )
|
|
.arg( myRasterViewPort->topLeftPoint.y() ).arg( static_cast<int>( myRasterViewPort->topLeftPoint.y() ) ),
|
|
3 );
|
|
|
|
//Set the transparency for the whole layer
|
|
//QImage::setAlphaChannel does not work quite as expected so set each pixel individually
|
|
//Currently this is only done for WMS images, which should be small enough not to impact performance
|
|
|
|
if ( mTransparencyLevel != 255 ) //improve performance if layer transparency not altered
|
|
{
|
|
QImage* transparentImageCopy = new QImage( *image ); //copy image if there is user transparency
|
|
image = transparentImageCopy;
|
|
int myWidth = image->width();
|
|
int myHeight = image->height();
|
|
QRgb myRgb;
|
|
int newTransparency;
|
|
for ( int myHeightRunner = 0; myHeightRunner < myHeight; myHeightRunner++ )
|
|
{
|
|
QRgb* myLineBuffer = ( QRgb* ) transparentImageCopy->scanLine( myHeightRunner );
|
|
for ( int myWidthRunner = 0; myWidthRunner < myWidth; myWidthRunner++ )
|
|
{
|
|
myRgb = image->pixel( myWidthRunner, myHeightRunner );
|
|
//combine transparency from WMS and layer transparency
|
|
newTransparency = ( double ) mTransparencyLevel / 255.0 * ( double )( qAlpha( myRgb ) );
|
|
myLineBuffer[ myWidthRunner ] = qRgba( qRed( myRgb ), qGreen( myRgb ), qBlue( myRgb ), newTransparency );
|
|
}
|
|
}
|
|
}
|
|
|
|
theQPainter->drawImage( myRasterViewPort->topLeftPoint.x(), myRasterViewPort->topLeftPoint.y() + currentPixelOffsetY, *image );
|
|
currentPixelOffsetY += numRowsPerPart;
|
|
|
|
if ( mTransparencyLevel != 255 )
|
|
{
|
|
delete image;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Otherwise use the old-fashioned GDAL direct-drawing style
|
|
// TODO: Move into its own GDAL provider.
|
|
|
|
// \/\/\/ - commented-out to handle zoomed-in rasters
|
|
// draw(theQPainter,myRasterViewPort);
|
|
// /\/\/\ - commented-out to handle zoomed-in rasters
|
|
// \/\/\/ - added to handle zoomed-in rasters
|
|
draw( theQPainter, myRasterViewPort, &theQgsMapToPixel );
|
|
// /\/\/\ - added to handle zoomed-in rasters
|
|
}
|
|
|
|
delete myRasterViewPort;
|
|
QgsDebugMsg( "exiting." );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
void QgsRasterLayer::draw( QPainter * theQPainter,
|
|
QgsRasterViewPort * theRasterViewPort,
|
|
const QgsMapToPixel* theQgsMapToPixel )
|
|
{
|
|
QgsDebugMsg( " 3 arguments" );
|
|
//
|
|
//
|
|
// The goal here is to make as many decisions as possible early on (outside of the rendering loop)
|
|
// so that we can maximise performance of the rendering process. So now we check which drawing
|
|
// procedure to use :
|
|
//
|
|
|
|
switch ( mDrawingStyle )
|
|
{
|
|
// a "Gray" or "Undefined" layer drawn as a range of gray colors
|
|
case SingleBandGray:
|
|
//check the band is set!
|
|
if ( mGrayBandName == TRSTRING_NOT_SET )
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
drawSingleBandGray( theQPainter, theRasterViewPort,
|
|
theQgsMapToPixel, bandNumber( mGrayBandName ) );
|
|
break;
|
|
}
|
|
// a "Gray" or "Undefined" layer drawn using a pseudocolor algorithm
|
|
case SingleBandPseudoColor:
|
|
//check the band is set!
|
|
if ( mGrayBandName == TRSTRING_NOT_SET )
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
drawSingleBandPseudoColor( theQPainter, theRasterViewPort,
|
|
theQgsMapToPixel, bandNumber( mGrayBandName ) );
|
|
break;
|
|
}
|
|
// a single band with a color map
|
|
case PalettedColor:
|
|
//check the band is set!
|
|
if ( mGrayBandName == TRSTRING_NOT_SET )
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
QgsDebugMsg( "PalettedColor drawing type detected..." );
|
|
|
|
drawPalettedSingleBandColor( theQPainter, theRasterViewPort,
|
|
theQgsMapToPixel, bandNumber( mGrayBandName ) );
|
|
|
|
break;
|
|
}
|
|
// a "Palette" layer drawn in gray scale (using only one of the color components)
|
|
case PalettedSingleBandGray:
|
|
//check the band is set!
|
|
if ( mGrayBandName == TRSTRING_NOT_SET )
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
QgsDebugMsg( "PalettedSingleBandGray drawing type detected..." );
|
|
|
|
int myBandNo = 1;
|
|
drawPalettedSingleBandGray( theQPainter, theRasterViewPort,
|
|
theQgsMapToPixel, myBandNo );
|
|
|
|
break;
|
|
}
|
|
// a "Palette" layer having only one of its color components rendered as psuedo color
|
|
case PalettedSingleBandPseudoColor:
|
|
//check the band is set!
|
|
if ( mGrayBandName == TRSTRING_NOT_SET )
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
|
|
int myBandNo = 1;
|
|
drawPalettedSingleBandPseudoColor( theQPainter, theRasterViewPort,
|
|
theQgsMapToPixel, myBandNo );
|
|
break;
|
|
}
|
|
//a "Palette" image where the bands contains 24bit color info and 8 bits is pulled out per color
|
|
case PalettedMultiBandColor:
|
|
drawPalettedMultiBandColor( theQPainter, theRasterViewPort,
|
|
theQgsMapToPixel, 1 );
|
|
break;
|
|
// a layer containing 2 or more bands, but using only one band to produce a grayscale image
|
|
case MultiBandSingleBandGray:
|
|
QgsDebugMsg( "MultiBandSingleBandGray drawing type detected..." );
|
|
//check the band is set!
|
|
if ( mGrayBandName == TRSTRING_NOT_SET )
|
|
{
|
|
QgsDebugMsg( "MultiBandSingleBandGray Not Set detected..." + mGrayBandName );
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
|
|
//get the band number for the mapped gray band
|
|
drawMultiBandSingleBandGray( theQPainter, theRasterViewPort,
|
|
theQgsMapToPixel, bandNumber( mGrayBandName ) );
|
|
break;
|
|
}
|
|
//a layer containing 2 or more bands, but using only one band to produce a pseudocolor image
|
|
case MultiBandSingleBandPseudoColor:
|
|
//check the band is set!
|
|
if ( mGrayBandName == TRSTRING_NOT_SET )
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
|
|
drawMultiBandSingleBandPseudoColor( theQPainter, theRasterViewPort,
|
|
theQgsMapToPixel, bandNumber( mGrayBandName ) );
|
|
break;
|
|
}
|
|
//a layer containing 2 or more bands, mapped to the three RGBcolors.
|
|
//In the case of a multiband with only two bands,
|
|
//one band will have to be mapped to more than one color
|
|
case MultiBandColor:
|
|
if ( mRedBandName == TRSTRING_NOT_SET ||
|
|
mGreenBandName == TRSTRING_NOT_SET ||
|
|
mBlueBandName == TRSTRING_NOT_SET )
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
drawMultiBandColor( theQPainter, theRasterViewPort,
|
|
theQgsMapToPixel );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
|
|
} //end of draw method
|
|
|
|
QString QgsRasterLayer::drawingStyleAsString() const
|
|
{
|
|
switch ( mDrawingStyle )
|
|
{
|
|
case SingleBandGray:
|
|
return QString( "SingleBandGray" ); //no need to tr() this its not shown in ui
|
|
break;
|
|
case SingleBandPseudoColor:
|
|
return QString( "SingleBandPseudoColor" );//no need to tr() this its not shown in ui
|
|
break;
|
|
case PalettedColor:
|
|
return QString( "PalettedColor" );//no need to tr() this its not shown in ui
|
|
break;
|
|
case PalettedSingleBandGray:
|
|
return QString( "PalettedSingleBandGray" );//no need to tr() this its not shown in ui
|
|
break;
|
|
case PalettedSingleBandPseudoColor:
|
|
return QString( "PalettedSingleBandPseudoColor" );//no need to tr() this its not shown in ui
|
|
break;
|
|
case PalettedMultiBandColor:
|
|
return QString( "PalettedMultiBandColor" );//no need to tr() this its not shown in ui
|
|
break;
|
|
case MultiBandSingleBandGray:
|
|
return QString( "MultiBandSingleBandGray" );//no need to tr() this its not shown in ui
|
|
break;
|
|
case MultiBandSingleBandPseudoColor:
|
|
return QString( "MultiBandSingleBandPseudoColor" );//no need to tr() this its not shown in ui
|
|
break;
|
|
case MultiBandColor:
|
|
return QString( "MultiBandColor" );//no need to tr() this its not shown in ui
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return QString( "UndefinedDrawingStyle" );
|
|
|
|
}
|
|
|
|
/**
|
|
* @note Note implemented yet
|
|
* @return always returns false
|
|
*/
|
|
bool QgsRasterLayer::hasCompatibleSymbology( const QgsMapLayer& theOther ) const
|
|
{
|
|
//preventwarnings
|
|
if ( theOther.type() < 0 )
|
|
{
|
|
return false;
|
|
}
|
|
return false;
|
|
} //todo
|
|
|
|
/**
|
|
* @param theBandNo The number of the band to check
|
|
* @return true if statistics have already been build for this band otherwise false
|
|
*/
|
|
bool QgsRasterLayer::hasStatistics( int theBandNo )
|
|
{
|
|
if ( theBandNo <= mRasterStatsList.size() && theBandNo > 0 )
|
|
{
|
|
//vector starts at base 0, band counts at base1 !
|
|
return mRasterStatsList[theBandNo - 1].statsGathered;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param thePoint the QgsPoint for which to obtain pixel values
|
|
* @param theResults QMap to hold the pixel values at thePoint for each layer in the raster file
|
|
* @return False if WMS layer and true otherwise
|
|
*/
|
|
bool QgsRasterLayer::identify( const QgsPoint& thePoint, QMap<QString, QString>& theResults )
|
|
{
|
|
theResults.clear();
|
|
if ( mProviderKey == "wms" )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
QgsDebugMsg( thePoint.toString() );
|
|
|
|
if ( !mProviderKey.isEmpty() )
|
|
{
|
|
QgsDebugMsg( "identify provider : " + mProviderKey ) ;
|
|
return ( mDataProvider->identify( thePoint, theResults ) );
|
|
}
|
|
|
|
if ( !mLayerExtent.contains( thePoint ) )
|
|
{
|
|
// Outside the raster
|
|
for ( int i = 1; i <= GDALGetRasterCount( mGdalDataset ); i++ )
|
|
{
|
|
theResults[ generateBandName( i )] = tr( "out of extent" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
double x = thePoint.x();
|
|
double y = thePoint.y();
|
|
|
|
/* Calculate the row / column where the point falls */
|
|
double xres = ( mLayerExtent.xMaximum() - mLayerExtent.xMinimum() ) / mWidth;
|
|
double yres = ( mLayerExtent.yMaximum() - mLayerExtent.yMinimum() ) / mHeight;
|
|
|
|
// Offset, not the cell index -> flor
|
|
int col = ( int ) floor(( x - mLayerExtent.xMinimum() ) / xres );
|
|
int row = ( int ) floor(( mLayerExtent.yMaximum() - y ) / yres );
|
|
|
|
QgsDebugMsg( "row = " + QString::number( row ) + " col = " + QString::number( col ) );
|
|
|
|
for ( int i = 1; i <= GDALGetRasterCount( mGdalDataset ); i++ )
|
|
{
|
|
GDALRasterBandH gdalBand = GDALGetRasterBand( mGdalDataset, i );
|
|
GDALDataType type = GDALGetRasterDataType( gdalBand );
|
|
int size = GDALGetDataTypeSize( type ) / 8;
|
|
void *data = CPLMalloc( size );
|
|
|
|
CPLErr err = GDALRasterIO( gdalBand, GF_Read, col, row, 1, 1,
|
|
data, 1, 1, type, 0, 0 );
|
|
|
|
if ( err != CPLE_None )
|
|
{
|
|
QgsLogger::warning( "RasterIO error: " + QString::fromUtf8( CPLGetLastErrorMsg() ) );
|
|
}
|
|
|
|
double value = readValue( data, type, 0 );
|
|
#ifdef QGISDEBUG
|
|
QgsLogger::debug( "value", value, 1, __FILE__, __FUNCTION__, __LINE__ );
|
|
#endif
|
|
QString v;
|
|
|
|
if ( mValidNoDataValue && ( qAbs( value - mNoDataValue ) <= TINY_VALUE || value != value ) )
|
|
{
|
|
v = tr( "null (no data)" );
|
|
}
|
|
else
|
|
{
|
|
v.setNum( value );
|
|
}
|
|
|
|
theResults[ generateBandName( i )] = v;
|
|
|
|
CPLFree( data );
|
|
}
|
|
}
|
|
|
|
return true;
|
|
} // bool QgsRasterLayer::identify
|
|
|
|
/**
|
|
* @note The arbitraryness of the returned document is enforced by WMS standards up to at least v1.3.0
|
|
*
|
|
* @param thePoint an image pixel coordinate in the last requested extent of layer.
|
|
* @return A text document containing the return from the WMS server
|
|
*/
|
|
QString QgsRasterLayer::identifyAsText( const QgsPoint& thePoint )
|
|
{
|
|
if ( mProviderKey != "wms" )
|
|
{
|
|
// Currently no meaning for anything other than OGC WMS layers
|
|
return QString();
|
|
}
|
|
|
|
return mDataProvider->identifyAsText( thePoint );
|
|
}
|
|
|
|
/**
|
|
* @note The arbitraryness of the returned document is enforced by WMS standards up to at least v1.3.0
|
|
*
|
|
* @param thePoint an image pixel coordinate in the last requested extent of layer.
|
|
* @return A html document containing the return from the WMS server
|
|
*/
|
|
QString QgsRasterLayer::identifyAsHtml( const QgsPoint& thePoint )
|
|
{
|
|
if ( mProviderKey != "wms" )
|
|
{
|
|
// Currently no meaning for anything other than OGC WMS layers
|
|
return QString();
|
|
}
|
|
|
|
return mDataProvider->identifyAsHtml( thePoint );
|
|
}
|
|
|
|
/**
|
|
* @note Note implemented yet
|
|
* @return Always returns false
|
|
*/
|
|
bool QgsRasterLayer::isEditable() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
QString QgsRasterLayer::lastError()
|
|
{
|
|
return mError;
|
|
}
|
|
|
|
QString QgsRasterLayer::lastErrorTitle()
|
|
{
|
|
return mErrorCaption;
|
|
}
|
|
|
|
/**
|
|
* This is an overloaded version of the legendAsPixmap( bool ) assumes false for the legend name flag.
|
|
* @return a pixmap representing a legend image
|
|
*/
|
|
QPixmap QgsRasterLayer::legendAsPixmap()
|
|
{
|
|
return legendAsPixmap( false );
|
|
}
|
|
|
|
/**
|
|
* @param theWithNameFlag - boolena flag whether to overlay the legend name in the text
|
|
* @return a pixmap representing a legend image
|
|
*/
|
|
QPixmap QgsRasterLayer::legendAsPixmap( bool theWithNameFlag )
|
|
{
|
|
QgsDebugMsg( "called (" + drawingStyleAsString() + ")" );
|
|
|
|
QPixmap myLegendQPixmap; //will be initialised once we know what drawing style is active
|
|
QPainter myQPainter;
|
|
|
|
|
|
if ( !mProviderKey.isEmpty() )
|
|
{
|
|
QgsDebugMsg( "provider Key (" + mProviderKey + ")" );
|
|
myLegendQPixmap = QPixmap( 3, 1 );
|
|
myQPainter.begin( &myLegendQPixmap );
|
|
//draw legend red part
|
|
myQPainter.setPen( QPen( QColor( 255, 0, 0 ), 0 ) );
|
|
myQPainter.drawPoint( 0, 0 );
|
|
//draw legend green part
|
|
myQPainter.setPen( QPen( QColor( 0, 255, 0 ), 0 ) );
|
|
myQPainter.drawPoint( 1, 0 );
|
|
//draw legend blue part
|
|
myQPainter.setPen( QPen( QColor( 0, 0, 255 ), 0 ) );
|
|
myQPainter.drawPoint( 2, 0 );
|
|
|
|
}
|
|
else
|
|
{
|
|
// Legacy GDAL (non-provider)
|
|
|
|
//
|
|
// Get the adjusted matrix stats
|
|
//
|
|
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, 1 );
|
|
QString myColorerpretation = GDALGetColorInterpretationName( GDALGetRasterColorInterpretation( myGdalBand ) );
|
|
|
|
|
|
|
|
//
|
|
// Create the legend pixmap - note it is generated on the preadjusted stats
|
|
//
|
|
if ( mDrawingStyle == MultiBandSingleBandGray || mDrawingStyle == PalettedSingleBandGray || mDrawingStyle == SingleBandGray )
|
|
{
|
|
|
|
myLegendQPixmap = QPixmap( 100, 1 );
|
|
myQPainter.begin( &myLegendQPixmap );
|
|
int myPos = 0;
|
|
for ( double my = 0; my < 255; my += 2.55 )
|
|
{
|
|
if ( !mInvertColor ) //histogram is not inverted
|
|
{
|
|
//draw legend as grayscale
|
|
int myGray = static_cast < int >( my );
|
|
myQPainter.setPen( QPen( QColor( myGray, myGray, myGray ), 0 ) );
|
|
}
|
|
else //histogram is inverted
|
|
{
|
|
//draw legend as inverted grayscale
|
|
int myGray = 255 - static_cast < int >( my );
|
|
myQPainter.setPen( QPen( QColor( myGray, myGray, myGray ), 0 ) );
|
|
} //end of invert histogram check
|
|
myQPainter.drawPoint( myPos++, 0 );
|
|
}
|
|
} //end of gray check
|
|
else if ( mDrawingStyle == MultiBandSingleBandPseudoColor ||
|
|
mDrawingStyle == PalettedSingleBandPseudoColor || mDrawingStyle == SingleBandPseudoColor )
|
|
{
|
|
|
|
//set up the three class breaks for pseudocolor mapping
|
|
double myRangeSize = 90; //hard coded for now
|
|
double myBreakSize = myRangeSize / 3;
|
|
double myClassBreakMin1 = 0;
|
|
double myClassBreakMax1 = myClassBreakMin1 + myBreakSize;
|
|
double myClassBreakMin2 = myClassBreakMax1;
|
|
double myClassBreakMax2 = myClassBreakMin2 + myBreakSize;
|
|
double myClassBreakMin3 = myClassBreakMax2;
|
|
|
|
//
|
|
// Create the legend pixmap - note it is generated on the preadjusted stats
|
|
//
|
|
myLegendQPixmap = QPixmap( 100, 1 );
|
|
myQPainter.begin( &myLegendQPixmap );
|
|
int myPos = 0;
|
|
for ( double my = 0; my < myRangeSize; my += myRangeSize / 100.0 )
|
|
{
|
|
//draw pseudocolor legend
|
|
if ( !mInvertColor )
|
|
{
|
|
//check if we are in the first class break
|
|
if (( my >= myClassBreakMin1 ) && ( my < myClassBreakMax1 ) )
|
|
{
|
|
int myRed = 0;
|
|
int myBlue = 255;
|
|
int myGreen = static_cast < int >((( 255 / myRangeSize ) * ( my - myClassBreakMin1 ) ) * 3 );
|
|
// testing this stuff still ...
|
|
if ( mColorShadingAlgorithm == FreakOutShader )
|
|
{
|
|
myRed = 255 - myGreen;
|
|
}
|
|
myQPainter.setPen( QPen( QColor( myRed, myGreen, myBlue ), 0 ) );
|
|
}
|
|
//check if we are in the second class break
|
|
else if (( my >= myClassBreakMin2 ) && ( my < myClassBreakMax2 ) )
|
|
{
|
|
int myRed = static_cast < int >((( 255 / myRangeSize ) * (( my - myClassBreakMin2 ) / 1 ) ) * 3 );
|
|
int myBlue = static_cast < int >( 255 - ((( 255 / myRangeSize ) * (( my - myClassBreakMin2 ) / 1 ) ) * 3 ) );
|
|
int myGreen = 255;
|
|
// testing this stuff still ...
|
|
if ( mColorShadingAlgorithm == FreakOutShader )
|
|
{
|
|
myGreen = myBlue;
|
|
}
|
|
myQPainter.setPen( QPen( QColor( myRed, myGreen, myBlue ), 0 ) );
|
|
}
|
|
//otherwise we must be in the third classbreak
|
|
else
|
|
{
|
|
int myRed = 255;
|
|
int myBlue = 0;
|
|
int myGreen = static_cast < int >( 255 - ((( 255 / myRangeSize ) * (( my - myClassBreakMin3 ) / 1 ) * 3 ) ) );
|
|
// testing this stuff still ...
|
|
if ( mColorShadingAlgorithm == FreakOutShader )
|
|
{
|
|
myRed = myGreen;
|
|
myGreen = 255 - myGreen;
|
|
}
|
|
myQPainter.setPen( QPen( QColor( myRed, myGreen, myBlue ), 0 ) );
|
|
}
|
|
} //end of invert !histogram false check
|
|
else //invert histogram toggle is off
|
|
{
|
|
//check if we are in the first class break
|
|
if (( my >= myClassBreakMin1 ) && ( my < myClassBreakMax1 ) )
|
|
{
|
|
int myRed = 255;
|
|
int myBlue = 0;
|
|
int myGreen = static_cast < int >((( 255 / myRangeSize ) * (( my - myClassBreakMin1 ) / 1 ) * 3 ) );
|
|
// testing this stuff still ...
|
|
if ( mColorShadingAlgorithm == FreakOutShader )
|
|
{
|
|
myRed = 255 - myGreen;
|
|
}
|
|
myQPainter.setPen( QPen( QColor( myRed, myGreen, myBlue ), 0 ) );
|
|
}
|
|
//check if we are in the second class break
|
|
else if (( my >= myClassBreakMin2 ) && ( my < myClassBreakMax2 ) )
|
|
{
|
|
int myRed = static_cast < int >( 255 - ((( 255 / myRangeSize ) * (( my - myClassBreakMin2 ) / 1 ) ) * 3 ) );
|
|
int myBlue = static_cast < int >((( 255 / myRangeSize ) * (( my - myClassBreakMin2 ) / 1 ) ) * 3 );
|
|
int myGreen = 255;
|
|
// testing this stuff still ...
|
|
if ( mColorShadingAlgorithm == FreakOutShader )
|
|
{
|
|
myGreen = myBlue;
|
|
}
|
|
myQPainter.setPen( QPen( QColor( myRed, myGreen, myBlue ), 0 ) );
|
|
}
|
|
//otherwise we must be in the third classbreak
|
|
else
|
|
{
|
|
int myRed = 0;
|
|
int myBlue = 255;
|
|
int myGreen = static_cast < int >( 255 - ((( 255 / myRangeSize ) * ( my - myClassBreakMin3 ) ) * 3 ) );
|
|
// testing this stuff still ...
|
|
if ( mColorShadingAlgorithm == FreakOutShader )
|
|
{
|
|
myRed = 255 - myGreen;
|
|
}
|
|
myQPainter.setPen( QPen( QColor( myRed, myGreen, myBlue ), 0 ) );
|
|
}
|
|
|
|
} //end of invert histogram check
|
|
myQPainter.drawPoint( myPos++, 0 );
|
|
}
|
|
|
|
} //end of pseudocolor check
|
|
else if ( mDrawingStyle == PalettedMultiBandColor || mDrawingStyle == MultiBandColor || mDrawingStyle == PalettedColor )
|
|
{
|
|
//
|
|
// Create the legend pixmap showing red green and blue band mappings
|
|
//
|
|
// TODO update this so it actually shows the mappings for paletted images
|
|
myLegendQPixmap = QPixmap( 3, 1 );
|
|
myQPainter.begin( &myLegendQPixmap );
|
|
//draw legend red part
|
|
myQPainter.setPen( QPen( QColor( 224, 103, 103 ), 0 ) );
|
|
myQPainter.drawPoint( 0, 0 );
|
|
//draw legend green part
|
|
myQPainter.setPen( QPen( QColor( 132, 224, 127 ), 0 ) );
|
|
myQPainter.drawPoint( 1, 0 );
|
|
//draw legend blue part
|
|
myQPainter.setPen( QPen( QColor( 127, 160, 224 ), 0 ) );
|
|
myQPainter.drawPoint( 2, 0 );
|
|
}
|
|
}
|
|
|
|
myQPainter.end();
|
|
|
|
|
|
// see if the caller wants the name of the layer in the pixmap (used for legend bar)
|
|
if ( theWithNameFlag )
|
|
{
|
|
QFont myQFont( "arial", 10, QFont::Normal );
|
|
QFontMetrics myQFontMetrics( myQFont );
|
|
|
|
int myHeight = ( myQFontMetrics.height() + 10 > 35 ) ? myQFontMetrics.height() + 10 : 35;
|
|
|
|
//create a matrix to
|
|
QMatrix myQWMatrix;
|
|
//scale the raster legend up a bit bigger to the legend item size
|
|
//note that scaling parameters are factors, not absolute values,
|
|
// so scale (0.25,1) scales the painter to a quarter of its size in the x direction
|
|
//TODO We need to decide how much to scale by later especially for rgb images which are only 3x1 pix
|
|
//hard coding thes values for now.
|
|
if ( myLegendQPixmap.width() == 3 )
|
|
{
|
|
//scale width by factor of 50 (=150px wide)
|
|
myQWMatrix.scale( 60, myHeight );
|
|
}
|
|
else
|
|
{
|
|
//assume 100px so scale by factor of 1.5 (=150px wide)
|
|
myQWMatrix.scale( 1.8, myHeight );
|
|
}
|
|
//apply the matrix
|
|
QPixmap myQPixmap2 = myLegendQPixmap.transformed( myQWMatrix );
|
|
QPainter myQPainter( &myQPixmap2 );
|
|
|
|
//load up the pyramid icons
|
|
QString myThemePath = QgsApplication::activeThemePath();
|
|
QPixmap myPyramidPixmap( myThemePath + "/mIconPyramid.png" );
|
|
QPixmap myNoPyramidPixmap( myThemePath + "/mIconNoPyramid.png" );
|
|
|
|
//
|
|
// Overlay a pyramid icon
|
|
//
|
|
if ( mHasPyramids )
|
|
{
|
|
myQPainter.drawPixmap( 0, myHeight - myPyramidPixmap.height(), myPyramidPixmap );
|
|
}
|
|
else
|
|
{
|
|
myQPainter.drawPixmap( 0, myHeight - myNoPyramidPixmap.height(), myNoPyramidPixmap );
|
|
}
|
|
//
|
|
// Overlay the layer name
|
|
//
|
|
if ( mDrawingStyle == MultiBandSingleBandGray || mDrawingStyle == PalettedSingleBandGray || mDrawingStyle == SingleBandGray )
|
|
{
|
|
myQPainter.setPen( Qt::white );
|
|
}
|
|
else
|
|
{
|
|
myQPainter.setPen( Qt::black );
|
|
}
|
|
myQPainter.setFont( myQFont );
|
|
myQPainter.drawText( 25, myHeight - 10, name() );
|
|
//
|
|
// finish up
|
|
//
|
|
myLegendQPixmap = myQPixmap2;
|
|
myQPainter.end();
|
|
}
|
|
//finish up
|
|
|
|
return myLegendQPixmap;
|
|
|
|
} //end of legendAsPixmap function
|
|
|
|
/**
|
|
* \param theLabelCount number of vertical labels to display
|
|
* @return a pixmap representing a legend image
|
|
*/
|
|
QPixmap QgsRasterLayer::legendAsPixmap( int theLabelCount )
|
|
{
|
|
QgsDebugMsg( "entered." );
|
|
QFont myQFont( "arial", 10, QFont::Normal );
|
|
QFontMetrics myQFontMetrics( myQFont );
|
|
|
|
int myFontHeight = ( myQFontMetrics.height() );
|
|
const int myerLabelSpacing = 5;
|
|
int myImageHeight = (( myFontHeight + ( myerLabelSpacing * 2 ) ) * theLabelCount );
|
|
//these next two vars are not used anywhere so commented out for now
|
|
//int myLongestLabelWidth = myQFontMetrics.width(name());
|
|
//const int myHorizontalLabelSpacing = 5;
|
|
const int myColorBarWidth = 10;
|
|
//
|
|
// Get the adjusted matrix stats
|
|
//
|
|
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, 1 );
|
|
QString myColorerpretation = GDALGetColorInterpretationName( GDALGetRasterColorInterpretation( myGdalBand ) );
|
|
QPixmap myLegendQPixmap; //will be initialised once we know what drawing style is active
|
|
QPainter myQPainter;
|
|
//
|
|
// Create the legend pixmap - note it is generated on the preadjusted stats
|
|
//
|
|
if ( mDrawingStyle == MultiBandSingleBandGray || mDrawingStyle == PalettedSingleBandGray || mDrawingStyle == SingleBandGray )
|
|
{
|
|
|
|
myLegendQPixmap = QPixmap( 1, myImageHeight );
|
|
const double myIncrement = static_cast<double>( myImageHeight ) / 255.0;
|
|
myQPainter.begin( &myLegendQPixmap );
|
|
int myPos = 0;
|
|
for ( double my = 0; my < 255; my += myIncrement )
|
|
{
|
|
if ( !mInvertColor ) //histogram is not inverted
|
|
{
|
|
//draw legend as grayscale
|
|
int myGray = static_cast < int >( my );
|
|
myQPainter.setPen( QPen( QColor( myGray, myGray, myGray ), 0 ) );
|
|
}
|
|
else //histogram is inverted
|
|
{
|
|
//draw legend as inverted grayscale
|
|
int myGray = 255 - static_cast < int >( my );
|
|
myQPainter.setPen( QPen( QColor( myGray, myGray, myGray ), 0 ) );
|
|
} //end of invert histogram check
|
|
myQPainter.drawPoint( 0, myPos++ );
|
|
}
|
|
} //end of gray check
|
|
else if ( mDrawingStyle == MultiBandSingleBandPseudoColor ||
|
|
mDrawingStyle == PalettedSingleBandPseudoColor || mDrawingStyle == SingleBandPseudoColor )
|
|
{
|
|
|
|
//set up the three class breaks for pseudocolor mapping
|
|
double myRangeSize = 90; //hard coded for now
|
|
double myBreakSize = myRangeSize / 3;
|
|
double myClassBreakMin1 = 0;
|
|
double myClassBreakMax1 = myClassBreakMin1 + myBreakSize;
|
|
double myClassBreakMin2 = myClassBreakMax1;
|
|
double myClassBreakMax2 = myClassBreakMin2 + myBreakSize;
|
|
double myClassBreakMin3 = myClassBreakMax2;
|
|
|
|
//
|
|
// Create the legend pixmap - note it is generated on the preadjusted stats
|
|
//
|
|
myLegendQPixmap = QPixmap( 1, myImageHeight );
|
|
const double myIncrement = myImageHeight / myRangeSize;
|
|
myQPainter.begin( &myLegendQPixmap );
|
|
int myPos = 0;
|
|
for ( double my = 0; my < 255; my += myIncrement )
|
|
for ( double my = 0; my < myRangeSize; my += myIncrement )
|
|
{
|
|
//draw pseudocolor legend
|
|
if ( !mInvertColor )
|
|
{
|
|
//check if we are in the first class break
|
|
if (( my >= myClassBreakMin1 ) && ( my < myClassBreakMax1 ) )
|
|
{
|
|
int myRed = 0;
|
|
int myBlue = 255;
|
|
int myGreen = static_cast < int >((( 255 / myRangeSize ) * ( my - myClassBreakMin1 ) ) * 3 );
|
|
// testing this stuff still ...
|
|
if ( mColorShadingAlgorithm == FreakOutShader )
|
|
{
|
|
myRed = 255 - myGreen;
|
|
}
|
|
myQPainter.setPen( QPen( QColor( myRed, myGreen, myBlue ), 0 ) );
|
|
}
|
|
//check if we are in the second class break
|
|
else if (( my >= myClassBreakMin2 ) && ( my < myClassBreakMax2 ) )
|
|
{
|
|
int myRed = static_cast < int >((( 255 / myRangeSize ) * (( my - myClassBreakMin2 ) / 1 ) ) * 3 );
|
|
int myBlue = static_cast < int >( 255 - ((( 255 / myRangeSize ) * (( my - myClassBreakMin2 ) / 1 ) ) * 3 ) );
|
|
int myGreen = 255;
|
|
// testing this stuff still ...
|
|
if ( mColorShadingAlgorithm == FreakOutShader )
|
|
{
|
|
myGreen = myBlue;
|
|
}
|
|
myQPainter.setPen( QPen( QColor( myRed, myGreen, myBlue ), 0 ) );
|
|
}
|
|
//otherwise we must be in the third classbreak
|
|
else
|
|
{
|
|
int myRed = 255;
|
|
int myBlue = 0;
|
|
int myGreen = static_cast < int >( 255 - ((( 255 / myRangeSize ) * (( my - myClassBreakMin3 ) / 1 ) * 3 ) ) );
|
|
// testing this stuff still ...
|
|
if ( mColorShadingAlgorithm == FreakOutShader )
|
|
{
|
|
myRed = myGreen;
|
|
myGreen = 255 - myGreen;
|
|
}
|
|
myQPainter.setPen( QPen( QColor( myRed, myGreen, myBlue ), 0 ) );
|
|
}
|
|
} //end of invert !histogram check
|
|
else //invert histogram toggle is off
|
|
{
|
|
//check if we are in the first class break
|
|
if (( my >= myClassBreakMin1 ) && ( my < myClassBreakMax1 ) )
|
|
{
|
|
int myRed = 255;
|
|
int myBlue = 0;
|
|
int myGreen = static_cast < int >((( 255 / myRangeSize ) * (( my - myClassBreakMin1 ) / 1 ) * 3 ) );
|
|
// testing this stuff still ...
|
|
if ( mColorShadingAlgorithm == FreakOutShader )
|
|
{
|
|
myRed = 255 - myGreen;
|
|
}
|
|
myQPainter.setPen( QPen( QColor( myRed, myGreen, myBlue ), 0 ) );
|
|
}
|
|
//check if we are in the second class break
|
|
else if (( my >= myClassBreakMin2 ) && ( my < myClassBreakMax2 ) )
|
|
{
|
|
int myRed = static_cast < int >( 255 - ((( 255 / myRangeSize ) * (( my - myClassBreakMin2 ) / 1 ) ) * 3 ) );
|
|
int myBlue = static_cast < int >((( 255 / myRangeSize ) * (( my - myClassBreakMin2 ) / 1 ) ) * 3 );
|
|
int myGreen = 255;
|
|
// testing this stuff still ...
|
|
if ( mColorShadingAlgorithm == FreakOutShader )
|
|
{
|
|
myGreen = myBlue;
|
|
}
|
|
myQPainter.setPen( QPen( QColor( myRed, myGreen, myBlue ), 0 ) );
|
|
}
|
|
//otherwise we must be in the third classbreak
|
|
else
|
|
{
|
|
int myRed = 0;
|
|
int myBlue = 255;
|
|
int myGreen = static_cast < int >( 255 - ((( 255 / myRangeSize ) * ( my - myClassBreakMin3 ) ) * 3 ) );
|
|
// testing this stuff still ...
|
|
if ( mColorShadingAlgorithm == FreakOutShader )
|
|
{
|
|
myRed = 255 - myGreen;
|
|
}
|
|
myQPainter.setPen( QPen( QColor( myRed, myGreen, myBlue ), 0 ) );
|
|
}
|
|
|
|
} //end of invert histogram check
|
|
myQPainter.drawPoint( 0, myPos++ );
|
|
}
|
|
|
|
} //end of pseudocolor check
|
|
else if ( mDrawingStyle == PalettedMultiBandColor || mDrawingStyle == MultiBandColor )
|
|
{
|
|
//
|
|
// Create the legend pixmap showing red green and blue band mappings
|
|
//
|
|
// TODO update this so it actually shows the mappings for paletted images
|
|
myLegendQPixmap = QPixmap( 1, 3 );
|
|
myQPainter.begin( &myLegendQPixmap );
|
|
//draw legend red part
|
|
myQPainter.setPen( QPen( QColor( 224, 103, 103 ), 0 ) );
|
|
myQPainter.drawPoint( 0, 0 );
|
|
//draw legend green part
|
|
myQPainter.setPen( QPen( QColor( 132, 224, 127 ), 0 ) );
|
|
myQPainter.drawPoint( 0, 1 );
|
|
//draw legend blue part
|
|
myQPainter.setPen( QPen( QColor( 127, 160, 224 ), 0 ) );
|
|
myQPainter.drawPoint( 0, 2 );
|
|
}
|
|
|
|
|
|
myQPainter.end();
|
|
|
|
|
|
|
|
//create a matrix to
|
|
QMatrix myQWMatrix;
|
|
//scale the raster legend up a bit bigger to the legend item size
|
|
//note that scaling parameters are factors, not absolute values,
|
|
// so scale (0.25,1) scales the painter to a quarter of its size in the x direction
|
|
//TODO We need to decide how much to scale by later especially for rgb images which are only 3x1 pix
|
|
//hard coding thes values for now.
|
|
if ( myLegendQPixmap.height() == 3 )
|
|
{
|
|
myQWMatrix.scale( myColorBarWidth, 2 );
|
|
}
|
|
else
|
|
{
|
|
myQWMatrix.scale( myColorBarWidth, 2 );
|
|
}
|
|
//apply the matrix
|
|
QPixmap myQPixmap2 = myLegendQPixmap.transformed( myQWMatrix );
|
|
QPainter myQPainter2( &myQPixmap2 );
|
|
//
|
|
// Overlay the layer name
|
|
//
|
|
if ( mDrawingStyle == MultiBandSingleBandGray || mDrawingStyle == PalettedSingleBandGray || mDrawingStyle == SingleBandGray )
|
|
{
|
|
myQPainter2.setPen( Qt::white );
|
|
}
|
|
else
|
|
{
|
|
myQPainter2.setPen( Qt::black );
|
|
}
|
|
myQPainter2.setFont( myQFont );
|
|
myQPainter2.drawText( 25, myImageHeight - 10, name() );
|
|
//
|
|
// finish up
|
|
//
|
|
myLegendQPixmap = myQPixmap2;
|
|
myQPainter2.end();
|
|
//finish up
|
|
|
|
return myLegendQPixmap;
|
|
|
|
}//end of getDetailedLegend
|
|
|
|
/**
|
|
* @param theBand the band number for which to get the maximum pixel value
|
|
* @return the maximum pixel value
|
|
*/
|
|
double QgsRasterLayer::maximumValue( unsigned int theBand )
|
|
{
|
|
if ( 0 < theBand && theBand <= bandCount() )
|
|
{
|
|
return mContrastEnhancementList[theBand - 1].maximumValue();
|
|
}
|
|
|
|
return 0.0;
|
|
}
|
|
|
|
/**
|
|
* @param theBand the band name for which to get the maximum pixel value
|
|
* @return the maximum pixel value
|
|
*/
|
|
double QgsRasterLayer::maximumValue( QString theBand )
|
|
{
|
|
if ( theBand != tr( "Not Set" ) )
|
|
{
|
|
return maximumValue( bandNumber( theBand ) );
|
|
}
|
|
|
|
return 0.0;
|
|
}
|
|
|
|
|
|
QString QgsRasterLayer::metadata()
|
|
{
|
|
QString myMetadata ;
|
|
myMetadata += "<p class=\"glossy\">" + tr( "Driver:" ) + "</p>\n";
|
|
myMetadata += "<p>";
|
|
if ( mProviderKey.isEmpty() )
|
|
{
|
|
myMetadata += QString( GDALGetDescription( GDALGetDatasetDriver( mGdalDataset ) ) );
|
|
myMetadata += "<br>";
|
|
myMetadata += QString( GDALGetMetadataItem( GDALGetDatasetDriver( mGdalDataset ), GDAL_DMD_LONGNAME, NULL ) );
|
|
}
|
|
else
|
|
{
|
|
myMetadata += mDataProvider->description();
|
|
}
|
|
myMetadata += "</p>\n";
|
|
|
|
if ( !mProviderKey.isEmpty() )
|
|
{
|
|
// Insert provider-specific (e.g. WMS-specific) metadata
|
|
myMetadata += mDataProvider->metadata();
|
|
}
|
|
else
|
|
{
|
|
|
|
// my added code (MColetti)
|
|
|
|
myMetadata += "<p class=\"glossy\">";
|
|
myMetadata += tr( "Dataset Description" );
|
|
myMetadata += "</p>\n";
|
|
myMetadata += "<p>";
|
|
myMetadata += QFile::decodeName( GDALGetDescription( mGdalDataset ) );
|
|
myMetadata += "</p>\n";
|
|
|
|
|
|
char ** GDALmetadata = GDALGetMetadata( mGdalDataset, NULL );
|
|
|
|
if ( GDALmetadata )
|
|
{
|
|
QStringList metadata = cStringList2Q_( GDALmetadata );
|
|
myMetadata += makeTableCells_( metadata );
|
|
}
|
|
else
|
|
{
|
|
QgsDebugMsg( "dataset has no metadata" );
|
|
}
|
|
|
|
for ( int i = 1; i <= GDALGetRasterCount( mGdalDataset ); ++i )
|
|
{
|
|
myMetadata += "<p class=\"glossy\">" + tr( "Band %1" ).arg( i ) + "</p>\n";
|
|
GDALRasterBandH gdalBand = GDALGetRasterBand( mGdalDataset, i );
|
|
GDALmetadata = GDALGetMetadata( gdalBand, NULL );
|
|
|
|
if ( GDALmetadata )
|
|
{
|
|
QStringList metadata = cStringList2Q_( GDALmetadata );
|
|
myMetadata += makeTableCells_( metadata );
|
|
}
|
|
else
|
|
{
|
|
QgsDebugMsg( "band " + QString::number( i ) + " has no metadata" );
|
|
}
|
|
|
|
char ** GDALcategories = GDALGetRasterCategoryNames( gdalBand );
|
|
|
|
if ( GDALcategories )
|
|
{
|
|
QStringList categories = cStringList2Q_( GDALcategories );
|
|
myMetadata += makeTableCells_( categories );
|
|
}
|
|
else
|
|
{
|
|
QgsDebugMsg( "band " + QString::number( i ) + " has no categories" );
|
|
}
|
|
|
|
}
|
|
|
|
// end my added code
|
|
|
|
myMetadata += "<p class=\"glossy\">";
|
|
myMetadata += tr( "Dimensions:" );
|
|
myMetadata += "</p>\n";
|
|
myMetadata += "<p>";
|
|
myMetadata += tr( "X: %1 Y: %2 Bands: %3" )
|
|
.arg( GDALGetRasterXSize( mGdalDataset ) )
|
|
.arg( GDALGetRasterYSize( mGdalDataset ) )
|
|
.arg( GDALGetRasterCount( mGdalDataset ) );
|
|
myMetadata += "</p>\n";
|
|
|
|
//just use the first band
|
|
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, 1 );
|
|
|
|
myMetadata += "<p class=\"glossy\">";
|
|
myMetadata += tr( "No Data Value" );
|
|
myMetadata += "</p>\n";
|
|
myMetadata += "<p>";
|
|
if ( mValidNoDataValue )
|
|
{
|
|
myMetadata += QString::number( mNoDataValue );
|
|
}
|
|
else
|
|
{
|
|
myMetadata += "*" + tr( "NoDataValue not set" ) + "*";
|
|
}
|
|
myMetadata += "</p>\n";
|
|
|
|
myMetadata += "</p>\n";
|
|
myMetadata += "<p class=\"glossy\">";
|
|
myMetadata += tr( "Data Type:" );
|
|
myMetadata += "</p>\n";
|
|
myMetadata += "<p>";
|
|
switch ( GDALGetRasterDataType( myGdalBand ) )
|
|
{
|
|
case GDT_Byte:
|
|
myMetadata += tr( "GDT_Byte - Eight bit unsigned integer" );
|
|
break;
|
|
case GDT_UInt16:
|
|
myMetadata += tr( "GDT_UInt16 - Sixteen bit unsigned integer " );
|
|
break;
|
|
case GDT_Int16:
|
|
myMetadata += tr( "GDT_Int16 - Sixteen bit signed integer " );
|
|
break;
|
|
case GDT_UInt32:
|
|
myMetadata += tr( "GDT_UInt32 - Thirty two bit unsigned integer " );
|
|
break;
|
|
case GDT_Int32:
|
|
myMetadata += tr( "GDT_Int32 - Thirty two bit signed integer " );
|
|
break;
|
|
case GDT_Float32:
|
|
myMetadata += tr( "GDT_Float32 - Thirty two bit floating point " );
|
|
break;
|
|
case GDT_Float64:
|
|
myMetadata += tr( "GDT_Float64 - Sixty four bit floating point " );
|
|
break;
|
|
case GDT_CInt16:
|
|
myMetadata += tr( "GDT_CInt16 - Complex Int16 " );
|
|
break;
|
|
case GDT_CInt32:
|
|
myMetadata += tr( "GDT_CInt32 - Complex Int32 " );
|
|
break;
|
|
case GDT_CFloat32:
|
|
myMetadata += tr( "GDT_CFloat32 - Complex Float32 " );
|
|
break;
|
|
case GDT_CFloat64:
|
|
myMetadata += tr( "GDT_CFloat64 - Complex Float64 " );
|
|
break;
|
|
default:
|
|
myMetadata += tr( "Could not determine raster data type." );
|
|
}
|
|
myMetadata += "</p>\n";
|
|
|
|
myMetadata += "<p class=\"glossy\">";
|
|
myMetadata += tr( "Pyramid overviews:" );
|
|
myMetadata += "</p>\n";
|
|
myMetadata += "<p>";
|
|
|
|
if ( GDALGetOverviewCount( myGdalBand ) > 0 )
|
|
{
|
|
int myOverviewInt;
|
|
for ( myOverviewInt = 0;
|
|
myOverviewInt < GDALGetOverviewCount( myGdalBand );
|
|
myOverviewInt++ )
|
|
{
|
|
GDALRasterBandH myOverview;
|
|
myOverview = GDALGetOverview( myGdalBand, myOverviewInt );
|
|
myMetadata += "<p>X : " + QString::number( GDALGetRasterBandXSize( myOverview ) );
|
|
myMetadata += ",Y " + QString::number( GDALGetRasterBandYSize( myOverview ) ) + "</p>";
|
|
}
|
|
}
|
|
myMetadata += "</p>\n";
|
|
} // if (mProviderKey.isEmpty())
|
|
|
|
myMetadata += "<p class=\"glossy\">";
|
|
myMetadata += tr( "Layer Spatial Reference System: " );
|
|
myMetadata += "</p>\n";
|
|
myMetadata += "<p>";
|
|
myMetadata += mCRS->toProj4();
|
|
myMetadata += "</p>\n";
|
|
|
|
// output coordinate system
|
|
// TODO: this is not related to layer, to be removed? [MD]
|
|
#if 0
|
|
myMetadata += "<tr><td class=\"glossy\">";
|
|
myMetadata += tr( "Project Spatial Reference System: " );
|
|
myMetadata += "</p>\n";
|
|
myMetadata += "<p>";
|
|
myMetadata += mCoordinateTransform->destCRS().toProj4();
|
|
myMetadata += "</p>\n";
|
|
#endif
|
|
|
|
if ( mProviderKey.isEmpty() )
|
|
{
|
|
if ( GDALGetGeoTransform( mGdalDataset, mGeoTransform ) != CE_None )
|
|
{
|
|
// if the raster does not have a valid transform we need to use
|
|
// a pixel size of (1,-1), but GDAL returns (1,1)
|
|
mGeoTransform[5] = -1;
|
|
}
|
|
else
|
|
{
|
|
myMetadata += "<p class=\"glossy\">";
|
|
myMetadata += tr( "Origin:" );
|
|
myMetadata += "</p>\n";
|
|
myMetadata += "<p>";
|
|
myMetadata += QString::number( mGeoTransform[0] );
|
|
myMetadata += ",";
|
|
myMetadata += QString::number( mGeoTransform[3] );
|
|
myMetadata += "</p>\n";
|
|
|
|
myMetadata += "<p class=\"glossy\">";
|
|
myMetadata += tr( "Pixel Size:" );
|
|
myMetadata += "</p>\n";
|
|
myMetadata += "<p>";
|
|
myMetadata += QString::number( mGeoTransform[1] );
|
|
myMetadata += ",";
|
|
myMetadata += QString::number( mGeoTransform[5] );
|
|
myMetadata += "</p>\n";
|
|
}
|
|
|
|
//
|
|
// Add the stats for each band to the output table
|
|
//
|
|
int myBandCountInt = bandCount();
|
|
for ( int myIteratorInt = 1; myIteratorInt <= myBandCountInt; ++myIteratorInt )
|
|
{
|
|
QgsDebugMsg( "Raster properties : checking if band " + QString::number( myIteratorInt ) + " has stats? " );
|
|
//band name
|
|
myMetadata += "<p class=\"glossy\">\n";
|
|
myMetadata += tr( "Band" );
|
|
myMetadata += "</p>\n";
|
|
myMetadata += "<p>";
|
|
myMetadata += bandName( myIteratorInt );
|
|
myMetadata += "</p>\n";
|
|
//band number
|
|
myMetadata += "<p>";
|
|
myMetadata += tr( "Band No" );
|
|
myMetadata += "</p>\n";
|
|
myMetadata += "<p>\n";
|
|
myMetadata += QString::number( myIteratorInt );
|
|
myMetadata += "</p>\n";
|
|
|
|
//check if full stats for this layer have already been collected
|
|
if ( !hasStatistics( myIteratorInt ) ) //not collected
|
|
{
|
|
QgsDebugMsg( ".....no" );
|
|
|
|
myMetadata += "<p>";
|
|
myMetadata += tr( "No Stats" );
|
|
myMetadata += "</p>\n";
|
|
myMetadata += "<p>\n";
|
|
myMetadata += tr( "No stats collected yet" );
|
|
myMetadata += "</p>\n";
|
|
}
|
|
else // collected - show full detail
|
|
{
|
|
QgsDebugMsg( ".....yes" );
|
|
|
|
QgsRasterBandStats myRasterBandStats = bandStatistics( myIteratorInt );
|
|
//Min Val
|
|
myMetadata += "<p>";
|
|
myMetadata += tr( "Min Val" );
|
|
myMetadata += "</p>\n";
|
|
myMetadata += "<p>\n";
|
|
myMetadata += QString::number( myRasterBandStats.minimumValue, 'f', 10 );
|
|
myMetadata += "</p>\n";
|
|
|
|
// Max Val
|
|
myMetadata += "<p>";
|
|
myMetadata += tr( "Max Val" );
|
|
myMetadata += "</p>\n";
|
|
myMetadata += "<p>\n";
|
|
myMetadata += QString::number( myRasterBandStats.maximumValue, 'f', 10 );
|
|
myMetadata += "</p>\n";
|
|
|
|
// Range
|
|
myMetadata += "<p>";
|
|
myMetadata += tr( "Range" );
|
|
myMetadata += "</p>\n";
|
|
myMetadata += "<p>\n";
|
|
myMetadata += QString::number( myRasterBandStats.range, 'f', 10 );
|
|
myMetadata += "</p>\n";
|
|
|
|
// Mean
|
|
myMetadata += "<p>";
|
|
myMetadata += tr( "Mean" );
|
|
myMetadata += "</p>\n";
|
|
myMetadata += "<p>\n";
|
|
myMetadata += QString::number( myRasterBandStats.mean, 'f', 10 );
|
|
myMetadata += "</p>\n";
|
|
|
|
//sum of squares
|
|
myMetadata += "<p>";
|
|
myMetadata += tr( "Sum of squares" );
|
|
myMetadata += "</p>\n";
|
|
myMetadata += "<p>\n";
|
|
myMetadata += QString::number( myRasterBandStats.sumOfSquares, 'f', 10 );
|
|
myMetadata += "</p>\n";
|
|
|
|
//standard deviation
|
|
myMetadata += "<p>";
|
|
myMetadata += tr( "Standard Deviation" );
|
|
myMetadata += "</p>\n";
|
|
myMetadata += "<p>\n";
|
|
myMetadata += QString::number( myRasterBandStats.stdDev, 'f', 10 );
|
|
myMetadata += "</p>\n";
|
|
|
|
//sum of all cells
|
|
myMetadata += "<p>";
|
|
myMetadata += tr( "Sum of all cells" );
|
|
myMetadata += "</p>\n";
|
|
myMetadata += "<p>\n";
|
|
myMetadata += QString::number( myRasterBandStats.sum, 'f', 10 );
|
|
myMetadata += "</p>\n";
|
|
|
|
//number of cells
|
|
myMetadata += "<p>";
|
|
myMetadata += tr( "Cell Count" );
|
|
myMetadata += "</p>\n";
|
|
myMetadata += "<p>\n";
|
|
myMetadata += QString::number( myRasterBandStats.elementCount );
|
|
myMetadata += "</p>\n";
|
|
}
|
|
}
|
|
} // if (mProviderKey.isEmpty())
|
|
|
|
QgsDebugMsg( myMetadata );
|
|
return myMetadata;
|
|
}
|
|
|
|
/**
|
|
* @param theBand the band number for which to get the minimum pixel value
|
|
* @return the minimum pixel value
|
|
*/
|
|
double QgsRasterLayer::minimumValue( unsigned int theBand )
|
|
{
|
|
if ( 0 < theBand && theBand <= bandCount() )
|
|
{
|
|
return mContrastEnhancementList[theBand - 1].minimumValue();
|
|
}
|
|
|
|
return 0.0;
|
|
}
|
|
|
|
/**
|
|
* @param theBand the band name for which to get the minimum pixel value
|
|
* @return the minimum pixel value
|
|
*/
|
|
double QgsRasterLayer::minimumValue( QString theBand )
|
|
{
|
|
return minimumValue( bandNumber( theBand ) );
|
|
}
|
|
|
|
/**
|
|
* @param theBandNumber the number of the band to use for generating a pixmap of the associated palette
|
|
* @return a 100x100 pixel QPixmap of the bands palette
|
|
*/
|
|
QPixmap QgsRasterLayer::paletteAsPixmap( int theBandNumber )
|
|
{
|
|
//TODO: This function should take dimensions
|
|
QgsDebugMsg( "entered." );
|
|
|
|
// Only do this for the non-provider (hard-coded GDAL) scenario...
|
|
// Maybe WMS can do this differently using QImage::numColors and QImage::color()
|
|
if ( mProviderKey.isEmpty() && hasBand( "Palette" ) && theBandNumber > 0 ) //don't tr() this its a gdal word!
|
|
{
|
|
QgsDebugMsg( "....found paletted image" );
|
|
QgsColorRampShader myShader;
|
|
QList<QgsColorRampShader::ColorRampItem> myColorRampItemList = myShader.colorRampItemList();
|
|
|
|
if ( readColorTable( 1, &myColorRampItemList ) )
|
|
{
|
|
QgsDebugMsg( "....got color ramp item list" );
|
|
myShader.setColorRampItemList( myColorRampItemList );
|
|
myShader.setColorRampType( QgsColorRampShader::DISCRETE );
|
|
// Draw image
|
|
int mySize = 100;
|
|
QPixmap myPalettePixmap( mySize, mySize );
|
|
QPainter myQPainter( &myPalettePixmap );
|
|
|
|
QImage myQImage = QImage( mySize, mySize, QImage::Format_RGB32 );
|
|
myQImage.fill( 0 );
|
|
myPalettePixmap.fill();
|
|
|
|
double myStep = (( double )myColorRampItemList.size() - 1 ) / ( double )( mySize * mySize );
|
|
double myValue = 0.0;
|
|
for ( int myRow = 0; myRow < mySize; myRow++ )
|
|
{
|
|
QRgb* myLineBuffer = ( QRgb* )myQImage.scanLine( myRow );
|
|
for ( int myCol = 0; myCol < mySize; myCol++ )
|
|
{
|
|
myValue = myStep * ( double )( myCol + myRow * mySize );
|
|
int c1, c2, c3;
|
|
myShader.shade( myValue, &c1, &c2, &c3 );
|
|
myLineBuffer[ myCol ] = qRgb( c1, c2, c3 );
|
|
}
|
|
}
|
|
|
|
myQPainter.drawImage( 0, 0, myQImage );
|
|
return myPalettePixmap;
|
|
}
|
|
QPixmap myNullPixmap;
|
|
return myNullPixmap;
|
|
}
|
|
else
|
|
{
|
|
//invalid layer was requested
|
|
QPixmap myNullPixmap;
|
|
return myNullPixmap;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* @param theBandNoInt - which band to compute the histogram for
|
|
* @param theBinCountInt - how many 'bins' to categorise the data into
|
|
* @param theIgnoreOutOfRangeFlag - whether to ignore values that are out of range (default=true)
|
|
* @param theThoroughBandScanFlag - whether to visit each cell when computing the histogram (default=false)
|
|
*/
|
|
void QgsRasterLayer::populateHistogram( int theBandNo, int theBinCount, bool theIgnoreOutOfRangeFlag, bool theHistogramEstimatedFlag )
|
|
{
|
|
|
|
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, theBandNo );
|
|
QgsRasterBandStats myRasterBandStats = bandStatistics( theBandNo );
|
|
//calculate the histogram for this band
|
|
//we assume that it only needs to be calculated if the length of the histogram
|
|
//vector is not equal to the number of bins
|
|
//i.e if the histogram has never previously been generated or the user has
|
|
//selected a new number of bins.
|
|
if ( myRasterBandStats.histogramVector->size() != theBinCount ||
|
|
theIgnoreOutOfRangeFlag != myRasterBandStats.isHistogramOutOfRange ||
|
|
theHistogramEstimatedFlag != myRasterBandStats.isHistogramEstimated )
|
|
{
|
|
myRasterBandStats.histogramVector->clear();
|
|
myRasterBandStats.isHistogramEstimated = theHistogramEstimatedFlag;
|
|
myRasterBandStats.isHistogramOutOfRange = theIgnoreOutOfRangeFlag;
|
|
int *myHistogramArray = new int[theBinCount];
|
|
|
|
|
|
/*
|
|
* CPLErr GDALRasterBand::GetHistogram (
|
|
* double dfMin,
|
|
* double dfMax,
|
|
* int nBuckets,
|
|
* int * panHistogram,
|
|
* int bIncludeOutOfRange,
|
|
* int bApproxOK,
|
|
* GDALProgressFunc pfnProgress,
|
|
* void * pProgressData
|
|
* )
|
|
*/
|
|
double myerval = ( myRasterBandStats.maximumValue - myRasterBandStats.minimumValue ) / theBinCount;
|
|
GDALGetRasterHistogram( myGdalBand, myRasterBandStats.minimumValue - 0.1*myerval,
|
|
myRasterBandStats.maximumValue + 0.1*myerval, theBinCount, myHistogramArray,
|
|
theIgnoreOutOfRangeFlag, theHistogramEstimatedFlag, progressCallback,
|
|
this ); //this is the arg for our custome gdal progress callback
|
|
|
|
for ( int myBin = 0; myBin < theBinCount; myBin++ )
|
|
{
|
|
myRasterBandStats.histogramVector->push_back( myHistogramArray[myBin] );
|
|
QgsDebugMsg( "Added " + QString::number( myHistogramArray[myBin] ) + " to histogram vector" );
|
|
}
|
|
|
|
}
|
|
QgsDebugMsg( ">>>>> Histogram vector now contains " + QString::number( myRasterBandStats.histogramVector->size() ) +
|
|
" elements" );
|
|
}
|
|
|
|
|
|
QString QgsRasterLayer::providerKey()
|
|
{
|
|
if ( mProviderKey.isEmpty() )
|
|
{
|
|
return QString();
|
|
}
|
|
else
|
|
{
|
|
return mProviderKey;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return the horizontal units per pixel as reported in the GDAL geotramsform[1]
|
|
*/
|
|
double QgsRasterLayer::rasterUnitsPerPixel()
|
|
{
|
|
// We return one raster pixel per map unit pixel
|
|
// One raster pixel can have several raster units...
|
|
|
|
// We can only use one of the mGeoTransform[], so go with the
|
|
// horisontal one.
|
|
|
|
return qAbs( mGeoTransform[1] );
|
|
}
|
|
|
|
/**
|
|
* @param theBandNumber the number of the band for which you want a color table
|
|
* @param theList a pointer the object that will hold the color table
|
|
* @return true of a color table was able to be read, false otherwise
|
|
*/
|
|
bool QgsRasterLayer::readColorTable( int theBandNumber, QList<QgsColorRampShader::ColorRampItem>* theList )
|
|
{
|
|
QgsDebugMsg( "entered." );
|
|
//Invalid band number, segfault prevention
|
|
if ( 0 >= theBandNumber || 0 == theList )
|
|
{
|
|
QgsDebugMsg( "Invalid parameter" );
|
|
return false;
|
|
}
|
|
|
|
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, theBandNumber );
|
|
GDALColorTableH myGdalColorTable = GDALGetRasterColorTable( myGdalBand );
|
|
|
|
if ( myGdalColorTable )
|
|
{
|
|
QgsDebugMsg( "Color table found" );
|
|
int myEntryCount = GDALGetColorEntryCount( myGdalColorTable );
|
|
GDALColorInterp myColorInterpretation = GDALGetRasterColorInterpretation( myGdalBand );
|
|
QgsDebugMsg( "Color Interpretation: " + QString::number(( int )myColorInterpretation ) );
|
|
GDALPaletteInterp myPaletteInterpretation = GDALGetPaletteInterpretation( myGdalColorTable );
|
|
QgsDebugMsg( "Palette Interpretation: " + QString::number(( int )myPaletteInterpretation ) );
|
|
|
|
const GDALColorEntry* myColorEntry = 0;
|
|
for ( int myIterator = 0; myIterator < myEntryCount; myIterator++ )
|
|
{
|
|
myColorEntry = GDALGetColorEntry( myGdalColorTable, myIterator );
|
|
|
|
if ( !myColorEntry )
|
|
{
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
//Branch on the color interpretation type
|
|
if ( myColorInterpretation == GCI_GrayIndex )
|
|
{
|
|
QgsColorRampShader::ColorRampItem myColorRampItem;
|
|
myColorRampItem.label = "";
|
|
myColorRampItem.value = ( double )myIterator;
|
|
myColorRampItem.color = QColor::fromRgb( myColorEntry->c1, myColorEntry->c1, myColorEntry->c1, myColorEntry->c4 );
|
|
theList->append( myColorRampItem );
|
|
}
|
|
else if ( myColorInterpretation == GCI_PaletteIndex )
|
|
{
|
|
QgsColorRampShader::ColorRampItem myColorRampItem;
|
|
myColorRampItem.label = "";
|
|
myColorRampItem.value = ( double )myIterator;
|
|
//Branch on palette interpretation
|
|
if ( myPaletteInterpretation == GPI_RGB )
|
|
{
|
|
myColorRampItem.color = QColor::fromRgb( myColorEntry->c1, myColorEntry->c2, myColorEntry->c3, myColorEntry->c4 );
|
|
}
|
|
else if ( myPaletteInterpretation == GPI_CMYK )
|
|
{
|
|
myColorRampItem.color = QColor::fromCmyk( myColorEntry->c1, myColorEntry->c2, myColorEntry->c3, myColorEntry->c4 );
|
|
}
|
|
else if ( myPaletteInterpretation == GPI_HLS )
|
|
{
|
|
myColorRampItem.color = QColor::fromHsv( myColorEntry->c1, myColorEntry->c3, myColorEntry->c2, myColorEntry->c4 );
|
|
}
|
|
else
|
|
{
|
|
myColorRampItem.color = QColor::fromRgb( myColorEntry->c1, myColorEntry->c1, myColorEntry->c1, myColorEntry->c4 );
|
|
}
|
|
theList->append( myColorRampItem );
|
|
}
|
|
else
|
|
{
|
|
QgsDebugMsg( "Color interpretation type not supported yet" );
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QgsDebugMsg( "No color table found for band " + QString::number( theBandNumber ) );
|
|
return false;
|
|
}
|
|
|
|
QgsDebugMsg( "Color table loaded successfully" );
|
|
return true;
|
|
}
|
|
|
|
void QgsRasterLayer::resetNoDataValue()
|
|
{
|
|
mNoDataValue = std::numeric_limits<int>::max();
|
|
mValidNoDataValue = false;
|
|
if ( mGdalDataset != NULL && GDALGetRasterCount( mGdalDataset ) > 0 )
|
|
{
|
|
int myRequestValid;
|
|
double myValue = GDALGetRasterNoDataValue(
|
|
GDALGetRasterBand( mGdalDataset, 1 ), &myRequestValid );
|
|
|
|
if ( 0 != myRequestValid )
|
|
{
|
|
setNoDataValue( myValue );
|
|
}
|
|
else
|
|
{
|
|
setNoDataValue( -9999.0 );
|
|
mValidNoDataValue = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void QgsRasterLayer::setBlueBandName( QString const & theBandName )
|
|
{
|
|
mBlueBandName = validateBandName( theBandName );
|
|
}
|
|
|
|
/** Copied from QgsVectorLayer::setDataProvider
|
|
* TODO: Make it work in the raster environment
|
|
*/
|
|
void QgsRasterLayer::setDataProvider( QString const & provider,
|
|
QStringList const & layers,
|
|
QStringList const & styles,
|
|
QString const & format,
|
|
QString const & crs )
|
|
{
|
|
// XXX should I check for and possibly delete any pre-existing providers?
|
|
// XXX How often will that scenario occur?
|
|
|
|
mProviderKey = provider; // XXX is this necessary? Usually already set
|
|
// XXX when execution gets here.
|
|
|
|
// load the plugin
|
|
QgsProviderRegistry * pReg = QgsProviderRegistry::instance();
|
|
QString ogrlib = pReg->library( provider );
|
|
|
|
//QString ogrlib = libDir + "/libpostgresprovider.so";
|
|
|
|
#ifdef TESTPROVIDERLIB
|
|
const char *cOgrLib = ( const char * ) ogrlib;
|
|
// test code to help debug provider loading problems
|
|
// void *handle = dlopen(cOgrLib, RTLD_LAZY);
|
|
void *handle = dlopen( cOgrLib, RTLD_LAZY | RTLD_GLOBAL );
|
|
if ( !handle )
|
|
{
|
|
QgsLogger::warning( "Error in dlopen: " );
|
|
}
|
|
else
|
|
{
|
|
QgsDebugMsg( "dlopen suceeded" );
|
|
dlclose( handle );
|
|
}
|
|
|
|
#endif
|
|
|
|
// load the data provider
|
|
mLib = new QLibrary( ogrlib );
|
|
QgsDebugMsg( "Library name is " + mLib->fileName() );
|
|
bool loaded = mLib->load();
|
|
|
|
if ( loaded )
|
|
{
|
|
QgsDebugMsg( "Loaded data provider library" );
|
|
QgsDebugMsg( "Attempting to resolve the classFactory function" );
|
|
classFactoryFunction_t * classFactory = ( classFactoryFunction_t * ) cast_to_fptr( mLib->resolve( "classFactory" ) );
|
|
|
|
mValid = false; // assume the layer is invalid until we
|
|
// determine otherwise
|
|
if ( classFactory )
|
|
{
|
|
QgsDebugMsg( "Getting pointer to a mDataProvider object from the library" );
|
|
//XXX - This was a dynamic cast but that kills the Windows
|
|
// version big-time with an abnormal termination error
|
|
// mDataProvider = (QgsRasterDataProvider*)(classFactory((const
|
|
// char*)(dataSource.utf8())));
|
|
|
|
// Copied from qgsproviderregistry in preference to the above.
|
|
mDataProvider = ( QgsRasterDataProvider* )( *classFactory )( &mDataSource );
|
|
|
|
if ( mDataProvider )
|
|
{
|
|
QgsDebugMsg( "Instantiated the data provider plugin" +
|
|
QString( " with layer list of " ) + layers.join( ", " ) + " and style list of " + styles.join( ", " ) +
|
|
" and format of " + format + " and CRS of " + crs );
|
|
mValid = mDataProvider->isValid();
|
|
|
|
if ( mValid )
|
|
{
|
|
mDataProvider->addLayers( layers, styles );
|
|
mValid = mDataProvider->isValid();
|
|
}
|
|
|
|
if ( mValid )
|
|
{
|
|
mDataProvider->setImageEncoding( format );
|
|
mDataProvider->setImageCrs( crs );
|
|
|
|
// get the extent
|
|
QgsRectangle mbr = mDataProvider->extent();
|
|
|
|
// show the extent
|
|
QString s = mbr.toString();
|
|
QgsDebugMsg( "Extent of layer: " + s );
|
|
// store the extent
|
|
mLayerExtent.setXMaximum( mbr.xMaximum() );
|
|
mLayerExtent.setXMinimum( mbr.xMinimum() );
|
|
mLayerExtent.setYMaximum( mbr.yMaximum() );
|
|
mLayerExtent.setYMinimum( mbr.yMinimum() );
|
|
|
|
// upper case the first letter of the layer name
|
|
QgsDebugMsg( "mLayerName: " + name() );
|
|
|
|
// set up the raster drawing style
|
|
mDrawingStyle = MultiBandColor; //sensible default
|
|
|
|
// Setup source CRS
|
|
if ( mProviderKey == "wms" )
|
|
{
|
|
*mCRS = QgsCoordinateReferenceSystem();
|
|
mCRS->createFromOgcWmsCrs( crs );
|
|
}
|
|
else
|
|
{
|
|
*mCRS = QgsCoordinateReferenceSystem( mDataProvider->crs() );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QgsLogger::warning( "QgsRasterLayer::setDataProvider: invalid layer" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QgsLogger::warning( "QgsRasterLayer::setDataProvider: Unable to instantiate the data provider plugin" );
|
|
mValid = false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mValid = false;
|
|
QgsLogger::warning( "QgsRasterLayer::setDataProvider: Failed to load ../providers/libproviders.so" );
|
|
|
|
}
|
|
QgsDebugMsg( "exiting." );
|
|
|
|
} // QgsRasterLayer::setDataProvider
|
|
|
|
void QgsRasterLayer::setColorShadingAlgorithm( ColorShadingAlgorithm theShadingAlgorithm )
|
|
{
|
|
QgsDebugMsg( "called with [" + QString::number( theShadingAlgorithm ) + "]" );
|
|
if ( mColorShadingAlgorithm != theShadingAlgorithm )
|
|
{
|
|
if ( 0 == mRasterShader )
|
|
{
|
|
mRasterShader = new QgsRasterShader();
|
|
}
|
|
|
|
switch ( theShadingAlgorithm )
|
|
{
|
|
case PseudoColorShader:
|
|
mRasterShader->setRasterShaderFunction( new QgsPseudoColorShader() );
|
|
break;
|
|
case FreakOutShader:
|
|
mRasterShader->setRasterShaderFunction( new QgsFreakOutShader() );
|
|
break;
|
|
case ColorRampShader:
|
|
mRasterShader->setRasterShaderFunction( new QgsColorRampShader() );
|
|
break;
|
|
case UserDefinedShader:
|
|
//do nothing
|
|
break;
|
|
default:
|
|
mRasterShader->setRasterShaderFunction( new QgsRasterShaderFunction() );
|
|
break;
|
|
}
|
|
|
|
//Set the class variable after the call to setRasterShader(), so memory recovery can happen
|
|
mColorShadingAlgorithm = theShadingAlgorithm;
|
|
}
|
|
QgsDebugMsg( "mColorShadingAlgorithm = " + QString::number( theShadingAlgorithm ) );
|
|
}
|
|
|
|
void QgsRasterLayer::setColorShadingAlgorithm( QString theShaderAlgorithm )
|
|
{
|
|
QgsDebugMsg( "called with [" + theShaderAlgorithm + "]" );
|
|
|
|
if ( theShaderAlgorithm == "PseudoColorShader" )
|
|
setColorShadingAlgorithm( PseudoColorShader );
|
|
else if ( theShaderAlgorithm == "FreakOutShader" )
|
|
setColorShadingAlgorithm( FreakOutShader );
|
|
else if ( theShaderAlgorithm == "ColorRampShader" )
|
|
setColorShadingAlgorithm( ColorRampShader );
|
|
else if ( theShaderAlgorithm == "UserDefinedShader" )
|
|
setColorShadingAlgorithm( UserDefinedShader );
|
|
else
|
|
setColorShadingAlgorithm( UndefinedShader );
|
|
}
|
|
|
|
void QgsRasterLayer::setContrastEnhancementAlgorithm( QgsContrastEnhancement::ContrastEnhancementAlgorithm theAlgorithm, bool theGenerateLookupTableFlag )
|
|
{
|
|
QList<QgsContrastEnhancement>::iterator myIterator = mContrastEnhancementList.begin();
|
|
while ( myIterator != mContrastEnhancementList.end() )
|
|
{
|
|
( *myIterator ).setContrastEnhancementAlgorithm( theAlgorithm, theGenerateLookupTableFlag );
|
|
++myIterator;
|
|
}
|
|
mContrastEnhancementAlgorithm = theAlgorithm;
|
|
}
|
|
|
|
void QgsRasterLayer::setContrastEnhancementAlgorithm( QString theAlgorithm, bool theGenerateLookupTableFlag )
|
|
{
|
|
QgsDebugMsg( "called with [" + theAlgorithm + "] and flag=" + QString::number(( int )theGenerateLookupTableFlag ) );
|
|
|
|
if ( theAlgorithm == "NoEnhancement" )
|
|
{
|
|
setContrastEnhancementAlgorithm( QgsContrastEnhancement::NoEnhancement, theGenerateLookupTableFlag );
|
|
}
|
|
else if ( theAlgorithm == "StretchToMinimumMaximum" )
|
|
{
|
|
setContrastEnhancementAlgorithm( QgsContrastEnhancement::StretchToMinimumMaximum, theGenerateLookupTableFlag );
|
|
}
|
|
else if ( theAlgorithm == "StretchAndClipToMinimumMaximum" )
|
|
{
|
|
setContrastEnhancementAlgorithm( QgsContrastEnhancement::StretchAndClipToMinimumMaximum, theGenerateLookupTableFlag );
|
|
}
|
|
else if ( theAlgorithm == "ClipToMinimumMaximum" )
|
|
{
|
|
setContrastEnhancementAlgorithm( QgsContrastEnhancement::ClipToMinimumMaximum, theGenerateLookupTableFlag );
|
|
}
|
|
else if ( theAlgorithm == "UserDefined" )
|
|
{
|
|
setContrastEnhancementAlgorithm( QgsContrastEnhancement::UserDefinedEnhancement, theGenerateLookupTableFlag );
|
|
}
|
|
else
|
|
{
|
|
setContrastEnhancementAlgorithm( QgsContrastEnhancement::NoEnhancement, theGenerateLookupTableFlag );
|
|
}
|
|
}
|
|
|
|
void QgsRasterLayer::setContrastEnhancementFunction( QgsContrastEnhancementFunction* theFunction )
|
|
{
|
|
if ( theFunction )
|
|
{
|
|
QList<QgsContrastEnhancement>::iterator myIterator = mContrastEnhancementList.begin();
|
|
while ( myIterator != mContrastEnhancementList.end() )
|
|
{
|
|
( *myIterator ).setContrastEnhancementFunction( theFunction );
|
|
++myIterator;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* Implemented mainly for serialisation / deserialisation of settings to xml.
|
|
* \note May be deprecated in the future! Use setDrawingStyle( DrawingStyle ) instead.
|
|
*/
|
|
void QgsRasterLayer::setDrawingStyle( QString const & theDrawingStyleQString )
|
|
{
|
|
if ( theDrawingStyleQString == "SingleBandGray" )//no need to tr() this its not shown in ui
|
|
{
|
|
mDrawingStyle = SingleBandGray;
|
|
}
|
|
else if ( theDrawingStyleQString == "SingleBandPseudoColor" )//no need to tr() this its not shown in ui
|
|
{
|
|
mDrawingStyle = SingleBandPseudoColor;
|
|
}
|
|
else if ( theDrawingStyleQString == "PalettedColor" )//no need to tr() this its not shown in ui
|
|
{
|
|
mDrawingStyle = PalettedColor;
|
|
}
|
|
else if ( theDrawingStyleQString == "PalettedSingleBandGray" )//no need to tr() this its not shown in ui
|
|
{
|
|
mDrawingStyle = PalettedSingleBandGray;
|
|
}
|
|
else if ( theDrawingStyleQString == "PalettedSingleBandPseudoColor" )//no need to tr() this its not shown in ui
|
|
{
|
|
mDrawingStyle = PalettedSingleBandPseudoColor;
|
|
}
|
|
else if ( theDrawingStyleQString == "PalettedMultiBandColor" )//no need to tr() this its not shown in ui
|
|
{
|
|
mDrawingStyle = PalettedMultiBandColor;
|
|
}
|
|
else if ( theDrawingStyleQString == "MultiBandSingleBandGray" )//no need to tr() this its not shown in ui
|
|
{
|
|
mDrawingStyle = MultiBandSingleBandGray;
|
|
}
|
|
else if ( theDrawingStyleQString == "MultiBandSingleBandPseudoColor" )//no need to tr() this its not shown in ui
|
|
{
|
|
mDrawingStyle = MultiBandSingleBandPseudoColor;
|
|
}
|
|
else if ( theDrawingStyleQString == "MultiBandColor" )//no need to tr() this its not shown in ui
|
|
{
|
|
mDrawingStyle = MultiBandColor;
|
|
}
|
|
else
|
|
{
|
|
mDrawingStyle = UndefinedDrawingStyle;
|
|
}
|
|
}
|
|
|
|
void QgsRasterLayer::setGrayBandName( QString const & theBandName )
|
|
{
|
|
mGrayBandName = validateBandName( theBandName );
|
|
}
|
|
|
|
void QgsRasterLayer::setGreenBandName( QString const & theBandName )
|
|
{
|
|
mGreenBandName = validateBandName( theBandName );
|
|
}
|
|
|
|
void QgsRasterLayer::setLayerOrder( QStringList const & layers )
|
|
{
|
|
QgsDebugMsg( "entered." );
|
|
|
|
if ( mDataProvider )
|
|
{
|
|
QgsDebugMsg( "About to mDataProvider->setLayerOrder(layers)." );
|
|
mDataProvider->setLayerOrder( layers );
|
|
}
|
|
|
|
}
|
|
|
|
void QgsRasterLayer::setMaximumValue( unsigned int theBand, double theValue, bool theGenerateLookupTableFlag )
|
|
{
|
|
if ( 0 < theBand && theBand <= bandCount() )
|
|
{
|
|
mContrastEnhancementList[theBand - 1].setMaximumValue( theValue, theGenerateLookupTableFlag );
|
|
}
|
|
}
|
|
|
|
void QgsRasterLayer::setMaximumValue( QString theBand, double theValue, bool theGenerateLookupTableFlag )
|
|
{
|
|
if ( theBand != tr( "Not Set" ) )
|
|
{
|
|
setMaximumValue( bandNumber( theBand ), theValue, theGenerateLookupTableFlag );
|
|
}
|
|
}
|
|
|
|
void QgsRasterLayer::setMinimumMaximumUsingLastExtent()
|
|
{
|
|
double myMinMax[2];
|
|
if ( rasterType() == QgsRasterLayer::GrayOrUndefined || drawingStyle() == QgsRasterLayer::SingleBandGray || drawingStyle() == QgsRasterLayer::MultiBandSingleBandGray )
|
|
{
|
|
computeMinimumMaximumFromLastExtent( grayBandName(), myMinMax );
|
|
setMinimumValue( grayBandName(), myMinMax[0] );
|
|
setMaximumValue( grayBandName(), myMinMax[1] );
|
|
|
|
setUserDefinedGrayMinimumMaximum( true );
|
|
}
|
|
else if ( rasterType() == QgsRasterLayer::Multiband )
|
|
{
|
|
computeMinimumMaximumFromLastExtent( redBandName(), myMinMax );
|
|
setMinimumValue( redBandName(), myMinMax[0], false );
|
|
setMaximumValue( redBandName(), myMinMax[1], false );
|
|
|
|
computeMinimumMaximumFromLastExtent( greenBandName(), myMinMax );
|
|
setMinimumValue( greenBandName(), myMinMax[0], false );
|
|
setMaximumValue( greenBandName(), myMinMax[1], false );
|
|
|
|
computeMinimumMaximumFromLastExtent( blueBandName(), myMinMax );
|
|
setMinimumValue( blueBandName(), myMinMax[0], false );
|
|
setMaximumValue( blueBandName(), myMinMax[1], false );
|
|
|
|
setUserDefinedRGBMinimumMaximum( true );
|
|
}
|
|
}
|
|
|
|
void QgsRasterLayer::setMinimumValue( unsigned int theBand, double theValue, bool theGenerateLookupTableFlag )
|
|
{
|
|
if ( 0 < theBand && theBand <= bandCount() )
|
|
{
|
|
mContrastEnhancementList[theBand - 1].setMinimumValue( theValue, theGenerateLookupTableFlag );
|
|
}
|
|
}
|
|
|
|
void QgsRasterLayer::setMinimumValue( QString theBand, double theValue, bool theGenerateLookupTableFlag )
|
|
{
|
|
if ( theBand != tr( "Not Set" ) )
|
|
{
|
|
setMinimumValue( bandNumber( theBand ), theValue, theGenerateLookupTableFlag );
|
|
}
|
|
|
|
}
|
|
|
|
void QgsRasterLayer::setNoDataValue( double theNoDataValue )
|
|
{
|
|
if ( theNoDataValue != mNoDataValue )
|
|
{
|
|
mNoDataValue = theNoDataValue;
|
|
mValidNoDataValue = true;
|
|
//Basically set the raster stats as invalid
|
|
QList<QgsRasterBandStats>::iterator myIterator = mRasterStatsList.begin();
|
|
while ( myIterator != mRasterStatsList.end() )
|
|
{
|
|
( *myIterator ).statsGathered = false;
|
|
++myIterator;
|
|
}
|
|
}
|
|
}
|
|
|
|
void QgsRasterLayer::setRasterShaderFunction( QgsRasterShaderFunction* theFunction )
|
|
{
|
|
if ( theFunction )
|
|
{
|
|
mRasterShader->setRasterShaderFunction( theFunction );
|
|
mColorShadingAlgorithm = QgsRasterLayer::UserDefinedShader;
|
|
}
|
|
else
|
|
{
|
|
//If NULL as passed in, set a default shader function to prevent segfaults
|
|
mRasterShader->setRasterShaderFunction( new QgsRasterShaderFunction() );
|
|
mColorShadingAlgorithm = QgsRasterLayer::UndefinedShader;
|
|
}
|
|
}
|
|
|
|
void QgsRasterLayer::setRedBandName( QString const & theBandName )
|
|
{
|
|
QgsDebugMsg( "setRedBandName : " + theBandName );
|
|
mRedBandName = validateBandName( theBandName );
|
|
}
|
|
|
|
void QgsRasterLayer::setSubLayerVisibility( QString const & name, bool vis )
|
|
{
|
|
|
|
if ( mDataProvider )
|
|
{
|
|
QgsDebugMsg( "About to mDataProvider->setSubLayerVisibility(name, vis)." );
|
|
mDataProvider->setSubLayerVisibility( name, vis );
|
|
}
|
|
|
|
}
|
|
|
|
void QgsRasterLayer::setTransparentBandName( QString const & theBandName )
|
|
{
|
|
mTransparencyBandName = validateBandName( theBandName );
|
|
}
|
|
|
|
void QgsRasterLayer::showProgress( int theValue )
|
|
{
|
|
emit progressUpdate( theValue );
|
|
}
|
|
|
|
|
|
void QgsRasterLayer::showStatusMessage( QString const & theMessage )
|
|
{
|
|
// QgsDebugMsg(QString("entered with '%1'.").arg(theMessage));
|
|
|
|
// Pass-through
|
|
// TODO: See if we can connect signal-to-signal. This is a kludge according to the Qt doc.
|
|
emit statusChanged( theMessage );
|
|
}
|
|
|
|
QStringList QgsRasterLayer::subLayers( GDALDatasetH dataset )
|
|
{
|
|
QStringList subLayers;
|
|
|
|
char **metadata = GDALGetMetadata( dataset, "SUBDATASETS" );
|
|
if ( metadata )
|
|
{
|
|
for ( int i = 0; metadata[i] != NULL; i++ )
|
|
{
|
|
QString layer = QString::fromUtf8( metadata[i] );
|
|
|
|
int pos = layer.indexOf( "_NAME=" );
|
|
if ( pos >= 0 )
|
|
{
|
|
subLayers << layer.mid( pos + 6 );
|
|
}
|
|
}
|
|
}
|
|
|
|
QgsDebugMsg( "sublayers:\n " + subLayers.join( "\n " ) );
|
|
|
|
return subLayers;
|
|
}
|
|
|
|
QStringList QgsRasterLayer::subLayers() const
|
|
{
|
|
if ( mDataProvider )
|
|
{
|
|
return mDataProvider->subLayers();
|
|
}
|
|
else
|
|
{
|
|
return subLayers( mGdalDataset );
|
|
}
|
|
}
|
|
|
|
void QgsRasterLayer::thumbnailAsPixmap( QPixmap * theQPixmap )
|
|
{
|
|
//TODO: This should be depreciated and a new function written that just returns a new QPixmap, it will be safer
|
|
if ( 0 == theQPixmap ) { return; }
|
|
|
|
theQPixmap->fill(); //defaults to white
|
|
|
|
// Raster providers are disabled (for the moment)
|
|
if ( mProviderKey.isEmpty() )
|
|
{
|
|
QgsRasterViewPort *myRasterViewPort = new QgsRasterViewPort();
|
|
myRasterViewPort->rectXOffset = 0;
|
|
myRasterViewPort->rectYOffset = 0;
|
|
myRasterViewPort->clippedXMin = 0;
|
|
myRasterViewPort->clippedXMax = mWidth;
|
|
myRasterViewPort->clippedYMin = mHeight;
|
|
myRasterViewPort->clippedYMax = 0;
|
|
myRasterViewPort->clippedWidth = mWidth;
|
|
myRasterViewPort->clippedHeight = mHeight;
|
|
myRasterViewPort->topLeftPoint = QgsPoint( 0, 0 );
|
|
myRasterViewPort->bottomRightPoint = QgsPoint( theQPixmap->width(), theQPixmap->height() );
|
|
myRasterViewPort->drawableAreaXDim = theQPixmap->width();
|
|
myRasterViewPort->drawableAreaYDim = theQPixmap->height();
|
|
|
|
QPainter * myQPainter = new QPainter( theQPixmap );
|
|
draw( myQPainter, myRasterViewPort );
|
|
delete myRasterViewPort;
|
|
myQPainter->end();
|
|
delete myQPainter;
|
|
}
|
|
|
|
}
|
|
|
|
void QgsRasterLayer::thumbnailAsImage( QImage * thepImage )
|
|
{
|
|
//TODO: This should be depreciated and a new function written that just returns a new QImage, it will be safer
|
|
if ( 0 == thepImage ) { return; }
|
|
|
|
thepImage->fill( Qt::white ); //defaults to white
|
|
|
|
// Raster providers are disabled (for the moment)
|
|
if ( mProviderKey.isEmpty() )
|
|
{
|
|
QgsRasterViewPort *myRasterViewPort = new QgsRasterViewPort();
|
|
myRasterViewPort->rectXOffset = 0;
|
|
myRasterViewPort->rectYOffset = 0;
|
|
myRasterViewPort->clippedXMin = 0;
|
|
myRasterViewPort->clippedXMax = mWidth;
|
|
myRasterViewPort->clippedYMin = mHeight;
|
|
myRasterViewPort->clippedYMax = 0;
|
|
myRasterViewPort->clippedWidth = mWidth;
|
|
myRasterViewPort->clippedHeight = mHeight;
|
|
myRasterViewPort->topLeftPoint = QgsPoint( 0, 0 );
|
|
myRasterViewPort->bottomRightPoint = QgsPoint( thepImage->width(), thepImage->height() );
|
|
myRasterViewPort->drawableAreaXDim = thepImage->width();
|
|
myRasterViewPort->drawableAreaYDim = thepImage->height();
|
|
|
|
QPainter * myQPainter = new QPainter( thepImage );
|
|
draw( myQPainter, myRasterViewPort );
|
|
delete myRasterViewPort;
|
|
myQPainter->end();
|
|
delete myQPainter;
|
|
}
|
|
|
|
}
|
|
|
|
void QgsRasterLayer::triggerRepaint()
|
|
{
|
|
emit repaintRequested();
|
|
}
|
|
|
|
void QgsRasterLayer::updateProgress( int theProgress, int theMax )
|
|
{
|
|
//simply propogate it on!
|
|
emit drawingProgress( theProgress, theMax );
|
|
}
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////
|
|
//
|
|
// Protected methods
|
|
//
|
|
/////////////////////////////////////////////////////////
|
|
/*
|
|
* @param QDomNode node that will contain the symbology definition for this layer.
|
|
* @param errorMessage reference to string that will be updated with any error messages
|
|
* @return true in case of success.
|
|
*/
|
|
bool QgsRasterLayer::readSymbology( const QDomNode& layer_node, QString& errorMessage )
|
|
{
|
|
QDomNode mnl = layer_node.namedItem( "rasterproperties" );
|
|
QDomNode snode = mnl.namedItem( "mDrawingStyle" );
|
|
QDomElement myElement = snode.toElement();
|
|
setDrawingStyle( myElement.text() );
|
|
|
|
snode = mnl.namedItem( "mColorShadingAlgorithm" );
|
|
myElement = snode.toElement();
|
|
setColorShadingAlgorithm( myElement.text() );
|
|
|
|
snode = mnl.namedItem( "mInvertColor" );
|
|
myElement = snode.toElement();
|
|
QVariant myVariant = ( QVariant ) myElement.attribute( "boolean" );
|
|
setInvertHistogram( myVariant.toBool() );
|
|
|
|
snode = mnl.namedItem( "mRedBandName" );
|
|
myElement = snode.toElement();
|
|
setRedBandName( myElement.text() );
|
|
|
|
snode = mnl.namedItem( "mGreenBandName" );
|
|
myElement = snode.toElement();
|
|
setGreenBandName( myElement.text() );
|
|
|
|
snode = mnl.namedItem( "mBlueBandName" );
|
|
myElement = snode.toElement();
|
|
setBlueBandName( myElement.text() );
|
|
|
|
snode = mnl.namedItem( "mGrayBandName" );
|
|
myElement = snode.toElement();
|
|
QgsDebugMsg( QString( " Setting gray band to : " ) + myElement.text() );
|
|
setGrayBandName( myElement.text() );
|
|
|
|
snode = mnl.namedItem( "mStandardDeviations" );
|
|
myElement = snode.toElement();
|
|
setStandardDeviations( myElement.text().toDouble() );
|
|
|
|
snode = mnl.namedItem( "mUserDefinedRGBMinimumMaximum" );
|
|
myElement = snode.toElement();
|
|
myVariant = ( QVariant ) myElement.attribute( "boolean" );
|
|
setUserDefinedRGBMinimumMaximum( myVariant.toBool() );
|
|
|
|
snode = mnl.namedItem( "mRGBMinimumMaximumEstimated" );
|
|
myElement = snode.toElement();
|
|
myVariant = ( QVariant ) myElement.attribute( "boolean" );
|
|
setRGBMinimumMaximumEstimated( myVariant.toBool() );
|
|
|
|
snode = mnl.namedItem( "mUserDefinedGrayMinimumMaximum" );
|
|
myElement = snode.toElement();
|
|
myVariant = ( QVariant ) myElement.attribute( "boolean" );
|
|
setUserDefinedGrayMinimumMaximum( myVariant.toBool() );
|
|
|
|
snode = mnl.namedItem( "mGrayMinimumMaximumEstimated" );
|
|
myElement = snode.toElement();
|
|
myVariant = ( QVariant ) myElement.attribute( "boolean" );
|
|
setGrayMinimumMaximumEstimated( myVariant.toBool() );
|
|
|
|
snode = mnl.namedItem( "mContrastEnhancementAlgorithm" );
|
|
myElement = snode.toElement();
|
|
setContrastEnhancementAlgorithm( myElement.text(), false );
|
|
|
|
QDomNode contrastEnhancementMinMaxValues = mnl.namedItem( "contrastEnhancementMinMaxValues" );
|
|
QDomNodeList minMaxValueList = contrastEnhancementMinMaxValues.toElement().elementsByTagName( "minMaxEntry" );
|
|
for ( int i = 0; i < minMaxValueList.size(); ++i )
|
|
{
|
|
QDomNode minMaxEntry = minMaxValueList.at( i ).toElement();
|
|
if ( minMaxEntry.isNull() )
|
|
{
|
|
continue;
|
|
}
|
|
QDomNode minEntry = minMaxEntry.namedItem( "min" );
|
|
QDomNode maxEntry = minMaxEntry.namedItem( "max" );
|
|
|
|
setMinimumValue( i + 1, minEntry.toElement().text().toDouble(), false );
|
|
setMaximumValue( i + 1, maxEntry.toElement().text().toDouble(), false );
|
|
}
|
|
|
|
QgsDebugMsg( "ReadXml: gray band name " + mGrayBandName );
|
|
QgsDebugMsg( "ReadXml: red band name " + mRedBandName );
|
|
QgsDebugMsg( "ReadXml: green band name " + mGreenBandName );
|
|
QgsDebugMsg( "Drawing style " + drawingStyleAsString() );
|
|
|
|
/*
|
|
* Transparency tab
|
|
*/
|
|
snode = mnl.namedItem( "mNoDataValue" );
|
|
myElement = snode.toElement();
|
|
setNoDataValue( myElement.text().toDouble() );
|
|
if ( myElement.attribute( "mValidNoDataValue", "false" ).compare( "true" ) )
|
|
{
|
|
// If flag element is not true, set to false.
|
|
mValidNoDataValue = false;
|
|
}
|
|
|
|
QDomNode singleValuePixelListNode = mnl.namedItem( "singleValuePixelList" );
|
|
if ( !singleValuePixelListNode.isNull() )
|
|
{
|
|
QList<QgsRasterTransparency::TransparentSingleValuePixel> newSingleValuePixelList;
|
|
|
|
//entries
|
|
QDomNodeList singleValuePixelList = singleValuePixelListNode.toElement().elementsByTagName( "pixelListEntry" );
|
|
for ( int i = 0; i < singleValuePixelList.size(); ++i )
|
|
{
|
|
QgsRasterTransparency::TransparentSingleValuePixel myNewItem;
|
|
QDomElement singleValuePixelListElement = singleValuePixelList.at( i ).toElement();
|
|
if ( singleValuePixelListElement.isNull() )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
myNewItem.pixelValue = singleValuePixelListElement.attribute( "pixelValue" ).toDouble();
|
|
myNewItem.percentTransparent = singleValuePixelListElement.attribute( "percentTransparent" ).toDouble();
|
|
|
|
newSingleValuePixelList.push_back( myNewItem );
|
|
}
|
|
mRasterTransparency.setTransparentSingleValuePixelList( newSingleValuePixelList );
|
|
}
|
|
|
|
QDomNode threeValuePixelListNode = mnl.namedItem( "threeValuePixelList" );
|
|
if ( !threeValuePixelListNode.isNull() )
|
|
{
|
|
QList<QgsRasterTransparency::TransparentThreeValuePixel> newThreeValuePixelList;
|
|
|
|
//entries
|
|
QDomNodeList threeValuePixelList = threeValuePixelListNode.toElement().elementsByTagName( "pixelListEntry" );
|
|
for ( int i = 0; i < threeValuePixelList.size(); ++i )
|
|
{
|
|
QgsRasterTransparency::TransparentThreeValuePixel myNewItem;
|
|
QDomElement threeValuePixelListElement = threeValuePixelList.at( i ).toElement();
|
|
if ( threeValuePixelListElement.isNull() )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
myNewItem.red = threeValuePixelListElement.attribute( "red" ).toDouble();
|
|
myNewItem.green = threeValuePixelListElement.attribute( "green" ).toDouble();
|
|
myNewItem.blue = threeValuePixelListElement.attribute( "blue" ).toDouble();
|
|
myNewItem.percentTransparent = threeValuePixelListElement.attribute( "percentTransparent" ).toDouble();
|
|
|
|
newThreeValuePixelList.push_back( myNewItem );
|
|
}
|
|
mRasterTransparency.setTransparentThreeValuePixelList( newThreeValuePixelList );
|
|
}
|
|
|
|
/*
|
|
* Color Ramp tab
|
|
*/
|
|
//restore custom color ramp settings
|
|
QDomNode customColorRampNode = mnl.namedItem( "customColorRamp" );
|
|
if ( !customColorRampNode.isNull() )
|
|
{
|
|
QgsColorRampShader* myColorRampShader = ( QgsColorRampShader* ) mRasterShader->rasterShaderFunction();
|
|
|
|
//TODO: Remove the customColorRampType check and following if() in v2.0, added for compatibility with older ( bugged ) project files
|
|
QDomNode customColorRampTypeNode = customColorRampNode.namedItem( "customColorRampType" );
|
|
QDomNode colorRampTypeNode = customColorRampNode.namedItem( "colorRampType" );
|
|
QString myRampType = "";
|
|
if ( "" == customColorRampTypeNode.toElement().text() )
|
|
{
|
|
myRampType = colorRampTypeNode.toElement().text();
|
|
}
|
|
else
|
|
{
|
|
myRampType = customColorRampTypeNode.toElement().text();
|
|
}
|
|
myColorRampShader->setColorRampType( myRampType );
|
|
|
|
|
|
//entries
|
|
QList<QgsColorRampShader::ColorRampItem> myColorRampItemList;
|
|
QDomNodeList colorRampEntryList = customColorRampNode.toElement().elementsByTagName( "colorRampEntry" );
|
|
for ( int i = 0; i < colorRampEntryList.size(); ++i )
|
|
{
|
|
QgsColorRampShader::ColorRampItem myNewItem;
|
|
QDomElement colorRampEntryElement = colorRampEntryList.at( i ).toElement();
|
|
if ( colorRampEntryElement.isNull() )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
myNewItem.color = QColor( colorRampEntryElement.attribute( "red" ).toInt(), colorRampEntryElement.attribute( "green" ).toInt(), colorRampEntryElement.attribute( "blue" ).toInt() );
|
|
myNewItem.label = colorRampEntryElement.attribute( "label" );
|
|
myNewItem.value = colorRampEntryElement.attribute( "value" ).toDouble();
|
|
|
|
myColorRampItemList.push_back( myNewItem );
|
|
}
|
|
myColorRampShader->setColorRampItemList( myColorRampItemList );
|
|
}
|
|
return true;
|
|
} //readSymbology
|
|
|
|
/**
|
|
|
|
Raster layer project file XML of form:
|
|
|
|
\verbatim
|
|
<maplayer type="raster" visible="1" showInOverviewFlag="1">
|
|
<layername>Wynoochee_dem</layername>
|
|
<datasource>/home/mcoletti/mnt/MCOLETTIF8F9/c/Toolkit_Course/Answers/Training_Data/wynoochee_dem.img</datasource>
|
|
<zorder>0</zorder>
|
|
<transparencyLevelInt>255</transparencyLevelInt>
|
|
<rasterproperties>
|
|
<mDrawingStyle>SingleBandGray</mDrawingStyle>
|
|
<mInvertColor boolean="false"/>
|
|
<mStandardDeviations>0</mStandardDeviations>
|
|
<mRedBandName>Not Set</mRedBandName>
|
|
<mGreenBandName>Not Set</mGreenBandName>
|
|
<mBlueBandName>Not Set</mBlueBandName>
|
|
<mGrayBandName>Undefined</mGrayBandName>
|
|
</rasterproperties>
|
|
</maplayer>
|
|
\endverbatim
|
|
|
|
@note Called by QgsMapLayer::readXML().
|
|
*/
|
|
bool QgsRasterLayer::readXml( QDomNode & layer_node )
|
|
{
|
|
//! @note Make sure to read the file first so stats etc are initialised properly!
|
|
|
|
//process provider key
|
|
QDomNode pkeyNode = layer_node.namedItem( "provider" );
|
|
|
|
if ( pkeyNode.isNull() )
|
|
{
|
|
mProviderKey = "";
|
|
}
|
|
else
|
|
{
|
|
QDomElement pkeyElt = pkeyNode.toElement();
|
|
mProviderKey = pkeyElt.text();
|
|
}
|
|
|
|
// Open the raster source based on provider and datasource
|
|
|
|
if ( !mProviderKey.isEmpty() )
|
|
{
|
|
// Go down the raster-data-provider paradigm
|
|
|
|
// Collect provider-specific information
|
|
|
|
QDomNode rpNode = layer_node.namedItem( "rasterproperties" );
|
|
|
|
// Collect sublayer names and styles
|
|
QStringList layers;
|
|
QStringList styles;
|
|
QDomElement layerElement = rpNode.firstChildElement( "wmsSublayer" );
|
|
while ( !layerElement.isNull() )
|
|
{
|
|
// TODO: sublayer visibility - post-0.8 release timeframe
|
|
|
|
// collect name for the sublayer
|
|
layers += layerElement.namedItem( "name" ).toElement().text();
|
|
|
|
// collect style for the sublayer
|
|
styles += layerElement.namedItem( "style" ).toElement().text();
|
|
|
|
layerElement = layerElement.nextSiblingElement( "wmsSublayer" );
|
|
}
|
|
|
|
// Collect format
|
|
QString format = rpNode.namedItem( "wmsFormat" ).toElement().text();
|
|
|
|
// Collect CRS
|
|
QString authid = crs().authid();
|
|
|
|
setDataProvider( mProviderKey, layers, styles, format, authid );
|
|
}
|
|
else
|
|
{
|
|
// Go down the monolithic-gdal-provider paradigm
|
|
|
|
if ( !readFile( source() ) ) // Data source name set in
|
|
// QgsMapLayer::readXML()
|
|
{
|
|
QgsLogger::warning( QString( __FILE__ ) + ":" + QString( __LINE__ ) +
|
|
" unable to read from raster file " + source() );
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
QString theError;
|
|
return readSymbology( layer_node, theError );
|
|
|
|
|
|
} // QgsRasterLayer::readXml( QDomNode & layer_node )
|
|
|
|
|
|
/*
|
|
* @param QDomNode the node that will have the style element added to it.
|
|
* @param QDomDocument the document that will have the QDomNode added.
|
|
* @param errorMessage reference to string that will be updated with any error messages
|
|
* @return true in case of success.
|
|
*/
|
|
bool QgsRasterLayer::writeSymbology( QDomNode & layer_node, QDomDocument & document, QString& errorMessage ) const
|
|
{
|
|
// <rasterproperties>
|
|
QDomElement rasterPropertiesElement = document.createElement( "rasterproperties" );
|
|
layer_node.appendChild( rasterPropertiesElement );
|
|
|
|
if ( !mProviderKey.isEmpty() )
|
|
{
|
|
QStringList sl = subLayers();
|
|
QStringList sls = mDataProvider->subLayerStyles();
|
|
|
|
QStringList::const_iterator layerStyle = sls.begin();
|
|
|
|
// <rasterproperties><wmsSublayer>
|
|
for ( QStringList::const_iterator layerName = sl.begin();
|
|
layerName != sl.end();
|
|
++layerName )
|
|
{
|
|
|
|
QgsDebugMsg( QString( "<rasterproperties><wmsSublayer> %1" ).arg( layerName->toLocal8Bit().data() ) );
|
|
|
|
QDomElement sublayerElement = document.createElement( "wmsSublayer" );
|
|
|
|
// TODO: sublayer visibility - post-0.8 release timeframe
|
|
|
|
// <rasterproperties><wmsSublayer><name>
|
|
QDomElement sublayerNameElement = document.createElement( "name" );
|
|
QDomText sublayerNameText = document.createTextNode( *layerName );
|
|
sublayerNameElement.appendChild( sublayerNameText );
|
|
sublayerElement.appendChild( sublayerNameElement );
|
|
|
|
// <rasterproperties><wmsSublayer><style>
|
|
QDomElement sublayerStyleElement = document.createElement( "style" );
|
|
QDomText sublayerStyleText = document.createTextNode( *layerStyle );
|
|
sublayerStyleElement.appendChild( sublayerStyleText );
|
|
sublayerElement.appendChild( sublayerStyleElement );
|
|
|
|
rasterPropertiesElement.appendChild( sublayerElement );
|
|
|
|
// This assumes there are exactly the same number of "layerName"s as there are "layerStyle"s
|
|
++layerStyle;
|
|
}
|
|
|
|
// <rasterproperties><wmsFormat>
|
|
QDomElement formatElement = document.createElement( "wmsFormat" );
|
|
QDomText formatText =
|
|
document.createTextNode( mDataProvider->imageEncoding() );
|
|
formatElement.appendChild( formatText );
|
|
rasterPropertiesElement.appendChild( formatElement );
|
|
|
|
}
|
|
|
|
// <mDrawingStyle>
|
|
QDomElement drawStyleElement = document.createElement( "mDrawingStyle" );
|
|
QDomText drawStyleText = document.createTextNode( drawingStyleAsString() );
|
|
|
|
drawStyleElement.appendChild( drawStyleText );
|
|
|
|
rasterPropertiesElement.appendChild( drawStyleElement );
|
|
|
|
// <colorShadingAlgorithm>
|
|
QDomElement colorShadingAlgorithmElement = document.createElement( "mColorShadingAlgorithm" );
|
|
QDomText colorShadingAlgorithmText = document.createTextNode( colorShadingAlgorithmAsString() );
|
|
|
|
colorShadingAlgorithmElement.appendChild( colorShadingAlgorithmText );
|
|
|
|
rasterPropertiesElement.appendChild( colorShadingAlgorithmElement );
|
|
|
|
// <mInvertColor>
|
|
QDomElement mInvertColorElement = document.createElement( "mInvertColor" );
|
|
|
|
if ( invertHistogram() )
|
|
{
|
|
mInvertColorElement.setAttribute( "boolean", "true" );
|
|
}
|
|
else
|
|
{
|
|
mInvertColorElement.setAttribute( "boolean", "false" );
|
|
}
|
|
|
|
rasterPropertiesElement.appendChild( mInvertColorElement );
|
|
|
|
|
|
// <mRedBandName>
|
|
QDomElement mRedBandNameElement = document.createElement( "mRedBandName" );
|
|
QString writtenRedBandName = redBandName();
|
|
if ( writtenRedBandName == TRSTRING_NOT_SET )
|
|
{
|
|
// Write english "not set" only.
|
|
writtenRedBandName = QSTRING_NOT_SET;
|
|
}
|
|
QDomText mRedBandNameText = document.createTextNode( writtenRedBandName );
|
|
|
|
mRedBandNameElement.appendChild( mRedBandNameText );
|
|
|
|
rasterPropertiesElement.appendChild( mRedBandNameElement );
|
|
|
|
|
|
// <mGreenBandName>
|
|
QDomElement mGreenBandNameElement = document.createElement( "mGreenBandName" );
|
|
QString writtenGreenBandName = greenBandName();
|
|
if ( writtenGreenBandName == TRSTRING_NOT_SET )
|
|
{
|
|
// Write english "not set" only.
|
|
writtenGreenBandName = QSTRING_NOT_SET;
|
|
}
|
|
QDomText mGreenBandNameText = document.createTextNode( writtenGreenBandName );
|
|
|
|
mGreenBandNameElement.appendChild( mGreenBandNameText );
|
|
|
|
rasterPropertiesElement.appendChild( mGreenBandNameElement );
|
|
|
|
|
|
// <mBlueBandName>
|
|
QDomElement mBlueBandNameElement = document.createElement( "mBlueBandName" );
|
|
QString writtenBlueBandName = blueBandName();
|
|
if ( writtenBlueBandName == TRSTRING_NOT_SET )
|
|
{
|
|
// Write english "not set" only.
|
|
writtenBlueBandName = QSTRING_NOT_SET;
|
|
}
|
|
QDomText mBlueBandNameText = document.createTextNode( writtenBlueBandName );
|
|
|
|
mBlueBandNameElement.appendChild( mBlueBandNameText );
|
|
|
|
rasterPropertiesElement.appendChild( mBlueBandNameElement );
|
|
|
|
|
|
// <mGrayBandName>
|
|
QDomElement mGrayBandNameElement = document.createElement( "mGrayBandName" );
|
|
QString writtenGrayBandName = grayBandName();
|
|
if ( writtenGrayBandName == TRSTRING_NOT_SET )
|
|
{
|
|
// Write english "not set" only.
|
|
writtenGrayBandName = QSTRING_NOT_SET;
|
|
}
|
|
QDomText mGrayBandNameText = document.createTextNode( writtenGrayBandName );
|
|
|
|
mGrayBandNameElement.appendChild( mGrayBandNameText );
|
|
rasterPropertiesElement.appendChild( mGrayBandNameElement );
|
|
|
|
// <mStandardDeviations>
|
|
QDomElement mStandardDeviationsElement = document.createElement( "mStandardDeviations" );
|
|
QDomText mStandardDeviationsText = document.createTextNode( QString::number( standardDeviations() ) );
|
|
|
|
mStandardDeviationsElement.appendChild( mStandardDeviationsText );
|
|
|
|
rasterPropertiesElement.appendChild( mStandardDeviationsElement );
|
|
|
|
// <mUserDefinedRGBMinimumMaximum>
|
|
QDomElement userDefinedRGBMinMaxFlag = document.createElement( "mUserDefinedRGBMinimumMaximum" );
|
|
|
|
if ( hasUserDefinedRGBMinimumMaximum() )
|
|
{
|
|
userDefinedRGBMinMaxFlag.setAttribute( "boolean", "true" );
|
|
}
|
|
else
|
|
{
|
|
userDefinedRGBMinMaxFlag.setAttribute( "boolean", "false" );
|
|
}
|
|
|
|
rasterPropertiesElement.appendChild( userDefinedRGBMinMaxFlag );
|
|
|
|
// <mRGBMinimumMaximumEstimated>
|
|
QDomElement RGBMinimumMaximumEstimated = document.createElement( "mRGBMinimumMaximumEstimated" );
|
|
|
|
if ( isRGBMinimumMaximumEstimated() )
|
|
{
|
|
RGBMinimumMaximumEstimated.setAttribute( "boolean", "true" );
|
|
}
|
|
else
|
|
{
|
|
RGBMinimumMaximumEstimated.setAttribute( "boolean", "false" );
|
|
}
|
|
|
|
rasterPropertiesElement.appendChild( RGBMinimumMaximumEstimated );
|
|
|
|
// <mUserDefinedGrayMinimumMaximum>
|
|
QDomElement userDefinedGrayMinMaxFlag = document.createElement( "mUserDefinedGrayMinimumMaximum" );
|
|
|
|
if ( hasUserDefinedGrayMinimumMaximum() )
|
|
{
|
|
userDefinedGrayMinMaxFlag.setAttribute( "boolean", "true" );
|
|
}
|
|
else
|
|
{
|
|
userDefinedGrayMinMaxFlag.setAttribute( "boolean", "false" );
|
|
}
|
|
|
|
rasterPropertiesElement.appendChild( userDefinedGrayMinMaxFlag );
|
|
|
|
// <mGrayMinimumMaximumEstimated>
|
|
QDomElement GrayMinimumMaximumEstimated = document.createElement( "mGrayMinimumMaximumEstimated" );
|
|
|
|
if ( isGrayMinimumMaximumEstimated() )
|
|
{
|
|
GrayMinimumMaximumEstimated.setAttribute( "boolean", "true" );
|
|
}
|
|
else
|
|
{
|
|
GrayMinimumMaximumEstimated.setAttribute( "boolean", "false" );
|
|
}
|
|
|
|
rasterPropertiesElement.appendChild( GrayMinimumMaximumEstimated );
|
|
|
|
// <contrastEnhancementAlgorithm>
|
|
QDomElement contrastEnhancementAlgorithmElement = document.createElement( "mContrastEnhancementAlgorithm" );
|
|
QDomText contrastEnhancementAlgorithmText = document.createTextNode( contrastEnhancementAlgorithmAsString() );
|
|
|
|
contrastEnhancementAlgorithmElement.appendChild( contrastEnhancementAlgorithmText );
|
|
|
|
rasterPropertiesElement.appendChild( contrastEnhancementAlgorithmElement );
|
|
|
|
// <minMaxValues>
|
|
QList<QgsContrastEnhancement>::const_iterator it;
|
|
QDomElement contrastEnhancementMinMaxValuesElement = document.createElement( "contrastEnhancementMinMaxValues" );
|
|
for ( it = mContrastEnhancementList.constBegin(); it != mContrastEnhancementList.constEnd(); ++it )
|
|
{
|
|
QDomElement minMaxEntry = document.createElement( "minMaxEntry" );
|
|
QDomElement minEntry = document.createElement( "min" );
|
|
QDomElement maxEntry = document.createElement( "max" );
|
|
|
|
QDomText minEntryText = document.createTextNode( QString::number( it->minimumValue() ) );
|
|
minEntry.appendChild( minEntryText );
|
|
|
|
QDomText maxEntryText = document.createTextNode( QString::number( it->maximumValue() ) );
|
|
maxEntry.appendChild( maxEntryText );
|
|
|
|
minMaxEntry.appendChild( minEntry );
|
|
minMaxEntry.appendChild( maxEntry );
|
|
|
|
contrastEnhancementMinMaxValuesElement.appendChild( minMaxEntry );
|
|
}
|
|
|
|
rasterPropertiesElement.appendChild( contrastEnhancementMinMaxValuesElement );
|
|
|
|
/*
|
|
* Transparency tab
|
|
*/
|
|
// <mNodataValue>
|
|
QDomElement mNoDataValueElement = document.createElement( "mNoDataValue" );
|
|
QDomText mNoDataValueText = document.createTextNode( QString::number( mNoDataValue, 'f' ) );
|
|
if ( mValidNoDataValue )
|
|
{
|
|
mNoDataValueElement.setAttribute( "mValidNoDataValue", "true" );
|
|
}
|
|
else
|
|
{
|
|
mNoDataValueElement.setAttribute( "mValidNoDataValue", "false" );
|
|
}
|
|
|
|
mNoDataValueElement.appendChild( mNoDataValueText );
|
|
|
|
rasterPropertiesElement.appendChild( mNoDataValueElement );
|
|
|
|
|
|
if ( mRasterTransparency.transparentSingleValuePixelList().count() > 0 )
|
|
{
|
|
QDomElement singleValuePixelListElement = document.createElement( "singleValuePixelList" );
|
|
|
|
|
|
QList<QgsRasterTransparency::TransparentSingleValuePixel> myPixelList = mRasterTransparency.transparentSingleValuePixelList();
|
|
QList<QgsRasterTransparency::TransparentSingleValuePixel>::iterator it;
|
|
for ( it = myPixelList.begin(); it != myPixelList.end(); ++it )
|
|
{
|
|
QDomElement pixelListElement = document.createElement( "pixelListEntry" );
|
|
pixelListElement.setAttribute( "pixelValue", QString::number( it->pixelValue, 'f' ) );
|
|
pixelListElement.setAttribute( "percentTransparent", QString::number( it->percentTransparent ) );
|
|
|
|
singleValuePixelListElement.appendChild( pixelListElement );
|
|
}
|
|
|
|
rasterPropertiesElement.appendChild( singleValuePixelListElement );
|
|
}
|
|
|
|
if ( mRasterTransparency.transparentThreeValuePixelList().count() > 0 )
|
|
{
|
|
QDomElement threeValuePixelListElement = document.createElement( "threeValuePixelList" );
|
|
|
|
|
|
QList<QgsRasterTransparency::TransparentThreeValuePixel> myPixelList = mRasterTransparency.transparentThreeValuePixelList();
|
|
QList<QgsRasterTransparency::TransparentThreeValuePixel>::iterator it;
|
|
for ( it = myPixelList.begin(); it != myPixelList.end(); ++it )
|
|
{
|
|
QDomElement pixelListElement = document.createElement( "pixelListEntry" );
|
|
pixelListElement.setAttribute( "red", QString::number( it->red, 'f' ) );
|
|
pixelListElement.setAttribute( "green", QString::number( it->green, 'f' ) );
|
|
pixelListElement.setAttribute( "blue", QString::number( it->blue, 'f' ) );
|
|
pixelListElement.setAttribute( "percentTransparent", QString::number( it->percentTransparent ) );
|
|
|
|
threeValuePixelListElement.appendChild( pixelListElement );
|
|
}
|
|
|
|
rasterPropertiesElement.appendChild( threeValuePixelListElement );
|
|
}
|
|
|
|
/*
|
|
* Color Ramp tab
|
|
*/
|
|
if ( QgsRasterLayer::ColorRampShader == colorShadingAlgorithm() )
|
|
{
|
|
QDomElement customColorRampElement = document.createElement( "customColorRamp" );
|
|
|
|
QDomElement customColorRampType = document.createElement( "colorRampType" );
|
|
QDomText customColorRampTypeText = document.createTextNode((( QgsColorRampShader* )mRasterShader->rasterShaderFunction() )->colorRampTypeAsQString() );
|
|
customColorRampType.appendChild( customColorRampTypeText );
|
|
customColorRampElement.appendChild( customColorRampType );
|
|
|
|
QList<QgsColorRampShader::ColorRampItem> myColorRampItemList = (( QgsColorRampShader* )mRasterShader->rasterShaderFunction() )->colorRampItemList();
|
|
QList<QgsColorRampShader::ColorRampItem>::iterator it;
|
|
for ( it = myColorRampItemList.begin(); it != myColorRampItemList.end(); ++it )
|
|
{
|
|
QDomElement colorRampEntryElement = document.createElement( "colorRampEntry" );
|
|
colorRampEntryElement.setAttribute( "red", QString::number( it->color.red() ) );
|
|
colorRampEntryElement.setAttribute( "green", QString::number( it->color.green() ) );
|
|
colorRampEntryElement.setAttribute( "blue", QString::number( it->color.blue() ) );
|
|
colorRampEntryElement.setAttribute( "value", QString::number( it->value, 'f' ) );
|
|
colorRampEntryElement.setAttribute( "label", it->label );
|
|
|
|
customColorRampElement.appendChild( colorRampEntryElement );
|
|
}
|
|
|
|
rasterPropertiesElement.appendChild( customColorRampElement );
|
|
}
|
|
|
|
return true;
|
|
} // bool QgsRasterLayer::writeSymbology
|
|
|
|
/*
|
|
* virtual
|
|
* @note Called by QgsMapLayer::writeXML().
|
|
*/
|
|
bool QgsRasterLayer::writeXml( QDomNode & layer_node,
|
|
QDomDocument & document )
|
|
{
|
|
// first get the layer element so that we can append the type attribute
|
|
|
|
QDomElement mapLayerNode = layer_node.toElement();
|
|
|
|
if ( mapLayerNode.isNull() || ( "maplayer" != mapLayerNode.nodeName() ) )
|
|
{
|
|
QgsLogger::warning( "QgsRasterLayer::writeXML() can't find <maplayer>" );
|
|
return false;
|
|
}
|
|
|
|
mapLayerNode.setAttribute( "type", "raster" );
|
|
|
|
// add provider node
|
|
|
|
QDomElement provider = document.createElement( "provider" );
|
|
QDomText providerText = document.createTextNode( mProviderKey );
|
|
provider.appendChild( providerText );
|
|
layer_node.appendChild( provider );
|
|
|
|
//write out the symbology
|
|
QString errorMsg;
|
|
return writeSymbology( layer_node, document, errorMsg );
|
|
}
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////
|
|
//
|
|
// Private methods
|
|
//
|
|
/////////////////////////////////////////////////////////
|
|
void QgsRasterLayer::drawMultiBandColor( QPainter * theQPainter, QgsRasterViewPort * theRasterViewPort,
|
|
const QgsMapToPixel* theQgsMapToPixel )
|
|
{
|
|
QgsDebugMsg( "entered." );
|
|
int myRedBandNo = bandNumber( mRedBandName );
|
|
//Invalid band number, segfault prevention
|
|
if ( 0 >= myRedBandNo )
|
|
{
|
|
return;
|
|
}
|
|
|
|
int myGreenBandNo = bandNumber( mGreenBandName );
|
|
//Invalid band number, segfault prevention
|
|
if ( 0 >= myGreenBandNo )
|
|
{
|
|
return;
|
|
}
|
|
|
|
int myBlueBandNo = bandNumber( mBlueBandName );
|
|
//Invalid band number, segfault prevention
|
|
if ( 0 >= myBlueBandNo )
|
|
{
|
|
return;
|
|
}
|
|
|
|
GDALRasterBandH myGdalRedBand = GDALGetRasterBand( mGdalDataset, myRedBandNo );
|
|
GDALRasterBandH myGdalGreenBand = GDALGetRasterBand( mGdalDataset, myGreenBandNo );
|
|
GDALRasterBandH myGdalBlueBand = GDALGetRasterBand( mGdalDataset, myBlueBandNo );
|
|
|
|
GDALDataType myRedType = GDALGetRasterDataType( myGdalRedBand );
|
|
GDALDataType myGreenType = GDALGetRasterDataType( myGdalGreenBand );
|
|
GDALDataType myBlueType = GDALGetRasterDataType( myGdalBlueBand );
|
|
|
|
QRgb* redImageScanLine = 0;
|
|
void* redRasterScanLine = 0;
|
|
QRgb* greenImageScanLine = 0;
|
|
void* greenRasterScanLine = 0;
|
|
QRgb* blueImageScanLine = 0;
|
|
void* blueRasterScanLine = 0;
|
|
|
|
QRgb myDefaultColor = qRgba( 255, 255, 255, 0 );
|
|
|
|
QgsRasterBandStats myRedBandStats;
|
|
QgsRasterBandStats myGreenBandStats;
|
|
QgsRasterBandStats myBlueBandStats;
|
|
|
|
/*
|
|
* If a stetch is requested and there are no user defined Min Max values
|
|
* we need to get these values from the bands themselves.
|
|
*
|
|
*/
|
|
if ( QgsContrastEnhancement::NoEnhancement != contrastEnhancementAlgorithm() && !mUserDefinedRGBMinimumMaximum && mStandardDeviations > 0 )
|
|
{
|
|
myRedBandStats = bandStatistics( myRedBandNo );
|
|
myGreenBandStats = bandStatistics( myGreenBandNo );
|
|
myBlueBandStats = bandStatistics( myBlueBandNo );
|
|
mRGBMinimumMaximumEstimated = false;
|
|
setMaximumValue( myRedBandNo, myRedBandStats.mean + ( mStandardDeviations * myRedBandStats.stdDev ) );
|
|
setMinimumValue( myRedBandNo, myRedBandStats.mean - ( mStandardDeviations * myRedBandStats.stdDev ) );
|
|
setMaximumValue( myGreenBandNo, myGreenBandStats.mean + ( mStandardDeviations * myGreenBandStats.stdDev ) );
|
|
setMinimumValue( myGreenBandNo, myGreenBandStats.mean - ( mStandardDeviations * myGreenBandStats.stdDev ) );
|
|
setMaximumValue( myBlueBandNo, myBlueBandStats.mean + ( mStandardDeviations * myBlueBandStats.stdDev ) );
|
|
setMinimumValue( myBlueBandNo, myBlueBandStats.mean - ( mStandardDeviations * myBlueBandStats.stdDev ) );
|
|
}
|
|
else if ( QgsContrastEnhancement::NoEnhancement != contrastEnhancementAlgorithm() && !mUserDefinedRGBMinimumMaximum )
|
|
{
|
|
//This case will be true the first time the image is loaded, so just approimate the min max to keep
|
|
//from calling generate raster band stats
|
|
double GDALrange[2];
|
|
mRGBMinimumMaximumEstimated = true;
|
|
|
|
GDALComputeRasterMinMax( myGdalRedBand, 1, GDALrange ); //Approximate
|
|
setMaximumValue( myRedBandNo, GDALrange[1] );
|
|
setMinimumValue( myRedBandNo, GDALrange[0] );
|
|
|
|
GDALComputeRasterMinMax( myGdalGreenBand, 1, GDALrange ); //Approximate
|
|
setMaximumValue( myGreenBandNo, GDALrange[1] );
|
|
setMinimumValue( myGreenBandNo, GDALrange[0] );
|
|
|
|
GDALComputeRasterMinMax( myGdalBlueBand, 1, GDALrange ); //Approximate
|
|
setMaximumValue( myBlueBandNo, GDALrange[1] );
|
|
setMinimumValue( myBlueBandNo, GDALrange[0] );
|
|
}
|
|
|
|
//Read and display pixels
|
|
double myRedValue = 0.0;
|
|
double myGreenValue = 0.0;
|
|
double myBlueValue = 0.0;
|
|
|
|
int myStretchedRedValue = 0;
|
|
int myStretchedGreenValue = 0;
|
|
int myStretchedBlueValue = 0;
|
|
int myAlphaValue = 0;
|
|
QgsContrastEnhancement* myRedContrastEnhancement = contrastEnhancement( myRedBandNo );
|
|
QgsContrastEnhancement* myGreenContrastEnhancement = contrastEnhancement( myGreenBandNo );
|
|
QgsContrastEnhancement* myBlueContrastEnhancement = contrastEnhancement( myBlueBandNo );
|
|
|
|
QgsRasterImageBuffer redImageBuffer( myGdalRedBand, theQPainter, theRasterViewPort, theQgsMapToPixel, &mGeoTransform[0] );
|
|
redImageBuffer.reset();
|
|
QgsRasterImageBuffer greenImageBuffer( myGdalGreenBand, theQPainter, theRasterViewPort, theQgsMapToPixel, &mGeoTransform[0] );
|
|
greenImageBuffer.setWritingEnabled( false ); //only draw to redImageBuffer
|
|
greenImageBuffer.reset();
|
|
QgsRasterImageBuffer blueImageBuffer( myGdalBlueBand, theQPainter, theRasterViewPort, theQgsMapToPixel, &mGeoTransform[0] );
|
|
blueImageBuffer.setWritingEnabled( false ); //only draw to redImageBuffer
|
|
blueImageBuffer.reset();
|
|
|
|
while ( redImageBuffer.nextScanLine( &redImageScanLine, &redRasterScanLine )
|
|
&& greenImageBuffer.nextScanLine( &greenImageScanLine, &greenRasterScanLine )
|
|
&& blueImageBuffer.nextScanLine( &blueImageScanLine, &blueRasterScanLine ) )
|
|
{
|
|
for ( int i = 0; i < theRasterViewPort->drawableAreaXDim; ++i )
|
|
{
|
|
myRedValue = readValue( redRasterScanLine, ( GDALDataType )myRedType, i );
|
|
myGreenValue = readValue( greenRasterScanLine, ( GDALDataType )myGreenType, i );
|
|
myBlueValue = readValue( blueRasterScanLine, ( GDALDataType )myBlueType, i );
|
|
|
|
if ( mValidNoDataValue &&
|
|
(
|
|
( qAbs( myRedValue - mNoDataValue ) <= TINY_VALUE || myRedValue != myRedValue ) ||
|
|
( qAbs( myGreenValue - mNoDataValue ) <= TINY_VALUE || myGreenValue != myGreenValue ) ||
|
|
( qAbs( myBlueValue - mNoDataValue ) <= TINY_VALUE || myBlueValue != myBlueValue )
|
|
)
|
|
)
|
|
{
|
|
redImageScanLine[ i ] = myDefaultColor;
|
|
continue;
|
|
}
|
|
|
|
if ( !myRedContrastEnhancement->isValueInDisplayableRange( myRedValue ) ||
|
|
!myGreenContrastEnhancement->isValueInDisplayableRange( myGreenValue ) ||
|
|
!myBlueContrastEnhancement->isValueInDisplayableRange( myBlueValue ) )
|
|
{
|
|
redImageScanLine[ i ] = myDefaultColor;
|
|
continue;
|
|
}
|
|
|
|
myAlphaValue = mRasterTransparency.alphaValue( myRedValue, myGreenValue, myBlueValue, mTransparencyLevel );
|
|
if ( 0 == myAlphaValue )
|
|
{
|
|
redImageScanLine[ i ] = myDefaultColor;
|
|
continue;
|
|
}
|
|
|
|
myStretchedRedValue = myRedContrastEnhancement->enhanceContrast( myRedValue );
|
|
myStretchedGreenValue = myGreenContrastEnhancement->enhanceContrast( myGreenValue );
|
|
myStretchedBlueValue = myBlueContrastEnhancement->enhanceContrast( myBlueValue );
|
|
|
|
if ( mInvertColor )
|
|
{
|
|
myStretchedRedValue = 255 - myStretchedRedValue;
|
|
myStretchedGreenValue = 255 - myStretchedGreenValue;
|
|
myStretchedBlueValue = 255 - myStretchedBlueValue;
|
|
}
|
|
|
|
redImageScanLine[ i ] = qRgba( myStretchedRedValue, myStretchedGreenValue, myStretchedBlueValue, myAlphaValue );
|
|
}
|
|
}
|
|
}
|
|
|
|
void QgsRasterLayer::drawMultiBandSingleBandGray( QPainter * theQPainter, QgsRasterViewPort * theRasterViewPort,
|
|
const QgsMapToPixel* theQgsMapToPixel, int theBandNo )
|
|
{
|
|
//delegate to drawSingleBandGray!
|
|
drawSingleBandGray( theQPainter, theRasterViewPort, theQgsMapToPixel, theBandNo );
|
|
}
|
|
|
|
void QgsRasterLayer::drawMultiBandSingleBandPseudoColor( QPainter * theQPainter, QgsRasterViewPort * theRasterViewPort,
|
|
const QgsMapToPixel* theQgsMapToPixel, int theBandNo )
|
|
{
|
|
//delegate to drawSinglePseudocolor!
|
|
drawSingleBandPseudoColor( theQPainter, theRasterViewPort, theQgsMapToPixel, theBandNo );
|
|
}
|
|
|
|
/**
|
|
* This method is used to render a single band with a color map.
|
|
* @param theQPainter pointer to the QPainter onto which the layer should be drawn.
|
|
* @param theRasterViewPort pointer to the ViewPort struct containing dimensions of viewable area and subset area to be extracted from data file.
|
|
* @param theQgsMapToPixel transformation coordinate to map canvas pixel
|
|
* @param theBandNo band number
|
|
*/
|
|
void QgsRasterLayer::drawPalettedSingleBandColor( QPainter * theQPainter, QgsRasterViewPort * theRasterViewPort,
|
|
const QgsMapToPixel* theQgsMapToPixel, int theBandNo )
|
|
{
|
|
QgsDebugMsg( "entered." );
|
|
//Invalid band number, segfault prevention
|
|
if ( 0 >= theBandNo )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( NULL == mRasterShader )
|
|
{
|
|
return;
|
|
}
|
|
|
|
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, theBandNo );
|
|
GDALDataType myDataType = GDALGetRasterDataType( myGdalBand );
|
|
|
|
QgsRasterImageBuffer imageBuffer( myGdalBand, theQPainter, theRasterViewPort, theQgsMapToPixel, &mGeoTransform[0] );
|
|
imageBuffer.reset();
|
|
|
|
QRgb* imageScanLine = 0;
|
|
void* rasterScanLine = 0;
|
|
|
|
QRgb myDefaultColor = qRgba( 255, 255, 255, 0 );
|
|
double myPixelValue = 0.0;
|
|
int myRedValue = 0;
|
|
int myGreenValue = 0;
|
|
int myBlueValue = 0;
|
|
int myAlphaValue = 0;
|
|
|
|
while ( imageBuffer.nextScanLine( &imageScanLine, &rasterScanLine ) )
|
|
{
|
|
for ( int i = 0; i < theRasterViewPort->drawableAreaXDim; ++i )
|
|
{
|
|
myRedValue = 0;
|
|
myGreenValue = 0;
|
|
myBlueValue = 0;
|
|
myPixelValue = readValue( rasterScanLine, ( GDALDataType )myDataType, i );
|
|
|
|
if ( mValidNoDataValue && ( qAbs( myPixelValue - mNoDataValue ) <= TINY_VALUE || myPixelValue != myPixelValue ) )
|
|
{
|
|
imageScanLine[ i ] = myDefaultColor;
|
|
continue;
|
|
}
|
|
|
|
myAlphaValue = mRasterTransparency.alphaValue( myPixelValue, mTransparencyLevel );
|
|
if ( 0 == myAlphaValue )
|
|
{
|
|
imageScanLine[ i ] = myDefaultColor;
|
|
continue;
|
|
}
|
|
|
|
if ( !mRasterShader->shade( myPixelValue, &myRedValue, &myGreenValue, &myBlueValue ) )
|
|
{
|
|
imageScanLine[ i ] = myDefaultColor;
|
|
continue;
|
|
}
|
|
|
|
if ( mInvertColor )
|
|
{
|
|
//Invert flag, flip blue and read
|
|
imageScanLine[ i ] = qRgba( myBlueValue, myGreenValue, myRedValue, myAlphaValue );
|
|
}
|
|
else
|
|
{
|
|
//Normal
|
|
imageScanLine[ i ] = qRgba( myRedValue, myGreenValue, myBlueValue, myAlphaValue );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This method is used to render a paletted raster layer as a gray image.
|
|
* @param theQPainter pointer to the QPainter onto which the layer should be drawn.
|
|
* @param theRasterViewPort pointer to the ViewPort struct containing dimensions of viewable area and subset area to be extracted from data file.
|
|
* @param theQgsMapToPixel transformation between map coordinates and canvas pixels
|
|
* @param theBandNo band number
|
|
*/
|
|
void QgsRasterLayer::drawPalettedSingleBandGray( QPainter * theQPainter, QgsRasterViewPort * theRasterViewPort,
|
|
const QgsMapToPixel* theQgsMapToPixel, int theBandNo )
|
|
{
|
|
QgsDebugMsg( "entered." );
|
|
//Invalid band number, segfault prevention
|
|
if ( 0 >= theBandNo )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( NULL == mRasterShader )
|
|
{
|
|
return;
|
|
}
|
|
|
|
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, theBandNo );
|
|
GDALDataType myDataType = GDALGetRasterDataType( myGdalBand );
|
|
|
|
QgsRasterImageBuffer imageBuffer( myGdalBand, theQPainter, theRasterViewPort, theQgsMapToPixel, &mGeoTransform[0] );
|
|
imageBuffer.reset();
|
|
|
|
QRgb* imageScanLine = 0;
|
|
void* rasterScanLine = 0;
|
|
|
|
QRgb myDefaultColor = qRgba( 255, 255, 255, 0 );
|
|
double myPixelValue = 0.0;
|
|
int myRedValue = 0;
|
|
int myGreenValue = 0;
|
|
int myBlueValue = 0;
|
|
int myAlphaValue = 0;
|
|
|
|
while ( imageBuffer.nextScanLine( &imageScanLine, &rasterScanLine ) )
|
|
{
|
|
for ( int i = 0; i < theRasterViewPort->drawableAreaXDim; ++i )
|
|
{
|
|
myRedValue = 0;
|
|
myGreenValue = 0;
|
|
myBlueValue = 0;
|
|
myPixelValue = readValue( rasterScanLine, ( GDALDataType )myDataType, i );
|
|
|
|
if ( mValidNoDataValue && ( qAbs( myPixelValue - mNoDataValue ) <= TINY_VALUE || myPixelValue != myPixelValue ) )
|
|
{
|
|
imageScanLine[ i ] = myDefaultColor;
|
|
continue;
|
|
}
|
|
|
|
myAlphaValue = mRasterTransparency.alphaValue( myPixelValue, mTransparencyLevel );
|
|
if ( 0 == myAlphaValue )
|
|
{
|
|
imageScanLine[ i ] = myDefaultColor;
|
|
continue;
|
|
}
|
|
|
|
if ( !mRasterShader->shade( myPixelValue, &myRedValue, &myGreenValue, &myBlueValue ) )
|
|
{
|
|
imageScanLine[ i ] = myDefaultColor;
|
|
continue;
|
|
}
|
|
|
|
if ( mInvertColor )
|
|
{
|
|
//Invert flag, flip blue and read
|
|
double myGrayValue = ( 0.3 * ( double )myRedValue ) + ( 0.59 * ( double )myGreenValue ) + ( 0.11 * ( double )myBlueValue );
|
|
imageScanLine[ i ] = qRgba(( int )myGrayValue, ( int )myGrayValue, ( int )myGrayValue, myAlphaValue );
|
|
}
|
|
else
|
|
{
|
|
//Normal
|
|
double myGrayValue = ( 0.3 * ( double )myBlueValue ) + ( 0.59 * ( double )myGreenValue ) + ( 0.11 * ( double )myRedValue );
|
|
imageScanLine[ i ] = qRgba(( int )myGrayValue, ( int )myGrayValue, ( int )myGrayValue, myAlphaValue );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This method is used to render a paletted raster layer as a pseudocolor image.
|
|
* @param theQPainter - pointer to the QPainter onto which the layer should be drawn.
|
|
* @param theRasterViewPort - pointer to the ViewPort struct containing dimensions of viewable area and subset area to be extracted from data file.
|
|
* @param theQgsMapToPixel transformation between map coordinates and canvas pixels
|
|
* @param theBandNo band number
|
|
gray.
|
|
*/
|
|
|
|
void QgsRasterLayer::drawPalettedSingleBandPseudoColor( QPainter * theQPainter, QgsRasterViewPort * theRasterViewPort,
|
|
const QgsMapToPixel* theQgsMapToPixel, int theBandNo )
|
|
{
|
|
QgsDebugMsg( "entered." );
|
|
//Invalid band number, segfault prevention
|
|
if ( 0 >= theBandNo )
|
|
{
|
|
return;
|
|
}
|
|
|
|
QgsRasterBandStats myRasterBandStats = bandStatistics( theBandNo );
|
|
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, theBandNo );
|
|
GDALDataType myDataType = GDALGetRasterDataType( myGdalBand );
|
|
|
|
QgsRasterImageBuffer imageBuffer( myGdalBand, theQPainter, theRasterViewPort, theQgsMapToPixel, &mGeoTransform[0] );
|
|
imageBuffer.reset();
|
|
|
|
QRgb* imageScanLine = 0;
|
|
void* rasterScanLine = 0;
|
|
|
|
QRgb myDefaultColor = qRgba( 255, 255, 255, 0 );
|
|
double myMinimumValue = 0.0;
|
|
double myMaximumValue = 0.0;
|
|
//Use standard deviations if set, otherwise, use min max of band
|
|
if ( mStandardDeviations > 0 )
|
|
{
|
|
myMinimumValue = ( myRasterBandStats.mean - ( mStandardDeviations * myRasterBandStats.stdDev ) );
|
|
myMaximumValue = ( myRasterBandStats.mean + ( mStandardDeviations * myRasterBandStats.stdDev ) );
|
|
}
|
|
else
|
|
{
|
|
myMinimumValue = myRasterBandStats.minimumValue;
|
|
myMaximumValue = myRasterBandStats.maximumValue;
|
|
}
|
|
|
|
mRasterShader->setMinimumValue( myMinimumValue );
|
|
mRasterShader->setMaximumValue( myMaximumValue );
|
|
|
|
double myPixelValue = 0.0;
|
|
int myRedValue = 0;
|
|
int myGreenValue = 0;
|
|
int myBlueValue = 0;
|
|
int myAlphaValue = 0;
|
|
|
|
while ( imageBuffer.nextScanLine( &imageScanLine, &rasterScanLine ) )
|
|
{
|
|
for ( int i = 0; i < theRasterViewPort->drawableAreaXDim; ++i )
|
|
{
|
|
myRedValue = 0;
|
|
myGreenValue = 0;
|
|
myBlueValue = 0;
|
|
myPixelValue = readValue( rasterScanLine, ( GDALDataType )myDataType, i );
|
|
|
|
if ( mValidNoDataValue && ( qAbs( myPixelValue - mNoDataValue ) <= TINY_VALUE || myPixelValue != myPixelValue ) )
|
|
{
|
|
imageScanLine[ i ] = myDefaultColor;
|
|
continue;
|
|
}
|
|
|
|
myAlphaValue = mRasterTransparency.alphaValue( myPixelValue, mTransparencyLevel );
|
|
if ( 0 == myAlphaValue )
|
|
{
|
|
imageScanLine[ i ] = myDefaultColor;
|
|
continue;
|
|
}
|
|
|
|
if ( !mRasterShader->shade( myPixelValue, &myRedValue, &myGreenValue, &myBlueValue ) )
|
|
{
|
|
imageScanLine[ i ] = myDefaultColor;
|
|
continue;
|
|
}
|
|
|
|
if ( mInvertColor )
|
|
{
|
|
//Invert flag, flip blue and read
|
|
imageScanLine[ i ] = qRgba( myBlueValue, myGreenValue, myRedValue, myAlphaValue );
|
|
}
|
|
else
|
|
{
|
|
//Normal
|
|
imageScanLine[ i ] = qRgba( myRedValue, myGreenValue, myBlueValue, myAlphaValue );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This method is used to render a paletted raster layer as a color image -- currently not supported
|
|
* @param theQPainter pointer to the QPainter onto which the layer should be drawn.
|
|
* @param theRasterViewPort pointer to the ViewPort struct containing dimensions of viewable area and subset area to be extracted from data file.
|
|
* @param theQgsMapToPixel transformation coordinate to map canvas pixel
|
|
* @param theBandNo pointer to the GDALRasterBand which should be rendered.
|
|
* @note not supported at this time
|
|
*/
|
|
void QgsRasterLayer::drawPalettedMultiBandColor( QPainter * theQPainter, QgsRasterViewPort * theRasterViewPort,
|
|
const QgsMapToPixel* theQgsMapToPixel, int theBandNo )
|
|
{
|
|
QgsDebugMsg( "Not supported at this time" );
|
|
}
|
|
|
|
void QgsRasterLayer::drawSingleBandGray( QPainter * theQPainter, QgsRasterViewPort * theRasterViewPort, const QgsMapToPixel* theQgsMapToPixel, int theBandNo )
|
|
{
|
|
QgsDebugMsg( "layer=" + QString::number( theBandNo ) );
|
|
//Invalid band number, segfault prevention
|
|
if ( 0 >= theBandNo )
|
|
{
|
|
return;
|
|
}
|
|
|
|
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, theBandNo );
|
|
GDALDataType myDataType = GDALGetRasterDataType( myGdalBand );
|
|
QgsRasterImageBuffer imageBuffer( myGdalBand, theQPainter, theRasterViewPort, theQgsMapToPixel, &mGeoTransform[0] );
|
|
imageBuffer.reset();
|
|
|
|
QRgb* imageScanLine = 0;
|
|
void* rasterScanLine = 0;
|
|
|
|
QRgb myDefaultColor = qRgba( 255, 255, 255, 0 );
|
|
double myGrayValue = 0.0;
|
|
int myGrayVal = 0;
|
|
int myAlphaValue = 0;
|
|
QgsContrastEnhancement* myContrastEnhancement = contrastEnhancement( theBandNo );
|
|
|
|
QgsRasterBandStats myGrayBandStats;
|
|
if ( QgsContrastEnhancement::NoEnhancement != contrastEnhancementAlgorithm() && !mUserDefinedGrayMinimumMaximum && mStandardDeviations > 0 )
|
|
{
|
|
mGrayMinimumMaximumEstimated = false;
|
|
myGrayBandStats = bandStatistics( theBandNo );
|
|
setMaximumValue( theBandNo, myGrayBandStats.mean + ( mStandardDeviations * myGrayBandStats.stdDev ) );
|
|
setMinimumValue( theBandNo, myGrayBandStats.mean - ( mStandardDeviations * myGrayBandStats.stdDev ) );
|
|
}
|
|
else if ( QgsContrastEnhancement::NoEnhancement != contrastEnhancementAlgorithm() && !mUserDefinedGrayMinimumMaximum )
|
|
{
|
|
//This case will be true the first time the image is loaded, so just approimate the min max to keep
|
|
//from calling generate raster band stats
|
|
double GDALrange[2];
|
|
GDALComputeRasterMinMax( myGdalBand, 1, GDALrange ); //Approximate
|
|
mGrayMinimumMaximumEstimated = true;
|
|
setMaximumValue( theBandNo, GDALrange[1] );
|
|
setMinimumValue( theBandNo, GDALrange[0] );
|
|
|
|
}
|
|
|
|
while ( imageBuffer.nextScanLine( &imageScanLine, &rasterScanLine ) )
|
|
{
|
|
for ( int i = 0; i < theRasterViewPort->drawableAreaXDim; ++i )
|
|
{
|
|
myGrayValue = readValue( rasterScanLine, ( GDALDataType )myDataType, i );
|
|
|
|
if ( mValidNoDataValue && ( qAbs( myGrayValue - mNoDataValue ) <= TINY_VALUE || myGrayValue != myGrayValue ) )
|
|
{
|
|
imageScanLine[ i ] = myDefaultColor;
|
|
continue;
|
|
}
|
|
|
|
if ( !myContrastEnhancement->isValueInDisplayableRange( myGrayValue ) )
|
|
{
|
|
imageScanLine[ i ] = myDefaultColor;
|
|
continue;
|
|
}
|
|
|
|
myAlphaValue = mRasterTransparency.alphaValue( myGrayValue, mTransparencyLevel );
|
|
if ( 0 == myAlphaValue )
|
|
{
|
|
imageScanLine[ i ] = myDefaultColor;
|
|
continue;
|
|
}
|
|
|
|
|
|
myGrayVal = myContrastEnhancement->enhanceContrast( myGrayValue );
|
|
|
|
if ( mInvertColor )
|
|
{
|
|
myGrayVal = 255 - myGrayVal;
|
|
}
|
|
|
|
imageScanLine[ i ] = qRgba( myGrayVal, myGrayVal, myGrayVal, myAlphaValue );
|
|
}
|
|
}
|
|
} // QgsRasterLayer::drawSingleBandGray
|
|
|
|
void QgsRasterLayer::drawSingleBandPseudoColor( QPainter * theQPainter,
|
|
QgsRasterViewPort * theRasterViewPort,
|
|
const QgsMapToPixel* theQgsMapToPixel,
|
|
int theBandNo )
|
|
{
|
|
QgsDebugMsg( "entered." );
|
|
//Invalid band number, segfault prevention
|
|
if ( 0 >= theBandNo )
|
|
{
|
|
return;
|
|
}
|
|
|
|
QgsRasterBandStats myRasterBandStats = bandStatistics( theBandNo );
|
|
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, theBandNo );
|
|
GDALDataType myDataType = GDALGetRasterDataType( myGdalBand );
|
|
|
|
QgsRasterImageBuffer imageBuffer( myGdalBand, theQPainter, theRasterViewPort, theQgsMapToPixel, &mGeoTransform[0] );
|
|
imageBuffer.reset();
|
|
|
|
QRgb* imageScanLine = 0;
|
|
void* rasterScanLine = 0;
|
|
|
|
QRgb myDefaultColor = qRgba( 255, 255, 255, 0 );
|
|
if ( NULL == mRasterShader )
|
|
{
|
|
return;
|
|
}
|
|
|
|
double myMinimumValue = 0.0;
|
|
double myMaximumValue = 0.0;
|
|
//Use standard deviations if set, otherwise, use min max of band
|
|
if ( mStandardDeviations > 0 )
|
|
{
|
|
myMinimumValue = ( myRasterBandStats.mean - ( mStandardDeviations * myRasterBandStats.stdDev ) );
|
|
myMaximumValue = ( myRasterBandStats.mean + ( mStandardDeviations * myRasterBandStats.stdDev ) );
|
|
}
|
|
else
|
|
{
|
|
myMinimumValue = myRasterBandStats.minimumValue;
|
|
myMaximumValue = myRasterBandStats.maximumValue;
|
|
}
|
|
|
|
mRasterShader->setMinimumValue( myMinimumValue );
|
|
mRasterShader->setMaximumValue( myMaximumValue );
|
|
|
|
int myRedValue = 255;
|
|
int myGreenValue = 255;
|
|
int myBlueValue = 255;
|
|
|
|
double myPixelValue = 0.0;
|
|
int myAlphaValue = 0;
|
|
|
|
while ( imageBuffer.nextScanLine( &imageScanLine, &rasterScanLine ) )
|
|
{
|
|
for ( int i = 0; i < theRasterViewPort->drawableAreaXDim; ++i )
|
|
{
|
|
myPixelValue = readValue( rasterScanLine, myDataType, i );
|
|
|
|
if ( mValidNoDataValue && ( qAbs( myPixelValue - mNoDataValue ) <= TINY_VALUE || myPixelValue != myPixelValue ) )
|
|
{
|
|
imageScanLine[ i ] = myDefaultColor;
|
|
continue;
|
|
}
|
|
|
|
myAlphaValue = mRasterTransparency.alphaValue( myPixelValue, mTransparencyLevel );
|
|
if ( 0 == myAlphaValue )
|
|
{
|
|
imageScanLine[ i ] = myDefaultColor;
|
|
continue;
|
|
}
|
|
|
|
if ( !mRasterShader->shade( myPixelValue, &myRedValue, &myGreenValue, &myBlueValue ) )
|
|
{
|
|
imageScanLine[ i ] = myDefaultColor;
|
|
continue;
|
|
}
|
|
|
|
if ( mInvertColor )
|
|
{
|
|
//Invert flag, flip blue and read
|
|
imageScanLine[ i ] = qRgba( myBlueValue, myGreenValue, myRedValue, myAlphaValue );
|
|
}
|
|
else
|
|
{
|
|
//Normal
|
|
imageScanLine[ i ] = qRgba( myRedValue, myGreenValue, myBlueValue, myAlphaValue );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void QgsRasterLayer::closeDataset()
|
|
{
|
|
if ( !mValid ) return;
|
|
mValid = false;
|
|
|
|
GDALDereferenceDataset( mGdalBaseDataset );
|
|
mGdalBaseDataset = NULL;
|
|
|
|
GDALClose( mGdalDataset );
|
|
mGdalDataset = NULL;
|
|
|
|
mHasPyramids = false;
|
|
mPyramidList.clear();
|
|
|
|
mRasterStatsList.clear();
|
|
}
|
|
|
|
QString QgsRasterLayer::generateBandName( int theBandNumber )
|
|
{
|
|
return tr( "Band" ) + QString( " %1" ) .arg( theBandNumber, 1 + ( int ) log10(( float ) bandCount() ), 10, QChar( '0' ) );
|
|
}
|
|
|
|
/**
|
|
* This method looks to see if a given band name exists.
|
|
*@note This function is no longer really needed and about to be removed
|
|
*/
|
|
bool QgsRasterLayer::hasBand( QString const & theBandName )
|
|
{
|
|
//TODO: This function is no longer really needed and about be removed -- it is only used to see if "Palette" exists which is not the correct way to see if a band is paletted or not
|
|
QgsDebugMsg( "Looking for band : " + theBandName );
|
|
|
|
for ( int i = 1; i <= GDALGetRasterCount( mGdalDataset ); i++ )
|
|
{
|
|
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, i );
|
|
QString myColorQString = GDALGetColorInterpretationName( GDALGetRasterColorInterpretation( myGdalBand ) );
|
|
#ifdef QGISDEBUG
|
|
QgsLogger::debug( "band", i, __FILE__, __FUNCTION__, __LINE__, 2 );
|
|
#endif
|
|
|
|
if ( myColorQString == theBandName )
|
|
{
|
|
#ifdef QGISDEBUG
|
|
QgsLogger::debug( "band", i, __FILE__, __FUNCTION__, __LINE__, 2 );
|
|
QgsDebugMsgLevel( "Found band : " + theBandName, 2 );
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
QgsDebugMsgLevel( "Found unmatched band : " + QString::number( i ) + " " + myColorQString, 2 );
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void QgsRasterLayer::paintImageToCanvas( QPainter* theQPainter, QgsRasterViewPort * theRasterViewPort, const QgsMapToPixel* theQgsMapToPixel, QImage* theImage )
|
|
{
|
|
// Set up the initial offset into the myQImage we want to copy to the map canvas
|
|
// This is useful when the source image pixels are larger than the screen image.
|
|
int paintXoffset = 0;
|
|
int paintYoffset = 0;
|
|
|
|
if ( theQgsMapToPixel )
|
|
{
|
|
paintXoffset = static_cast<int>(
|
|
( theRasterViewPort->rectXOffsetFloat -
|
|
theRasterViewPort->rectXOffset )
|
|
/ theQgsMapToPixel->mapUnitsPerPixel()
|
|
* qAbs( mGeoTransform[1] )
|
|
);
|
|
|
|
paintYoffset = static_cast<int>(
|
|
( theRasterViewPort->rectYOffsetFloat -
|
|
theRasterViewPort->rectYOffset )
|
|
/ theQgsMapToPixel->mapUnitsPerPixel()
|
|
* qAbs( mGeoTransform[5] )
|
|
);
|
|
}
|
|
|
|
|
|
|
|
QgsDebugMsg( "painting image to canvas from "
|
|
+ QString::number( paintXoffset ) + ", " + QString::number( paintYoffset )
|
|
+ " to "
|
|
+ QString::number( static_cast<int>( theRasterViewPort->topLeftPoint.x() + 0.5 ) )
|
|
+ ", "
|
|
+ QString::number( static_cast<int>( theRasterViewPort->topLeftPoint.y() + 0.5 ) )
|
|
+ "." );
|
|
|
|
//Catch special rendering cases
|
|
//INSTANCE: 1x1
|
|
if ( 1 == theRasterViewPort->clippedWidth && 1 == theRasterViewPort->clippedHeight )
|
|
{
|
|
QColor myColor( theImage->pixel( 0, 0 ) );
|
|
myColor.setAlpha( qAlpha( theImage->pixel( 0, 0 ) ) );
|
|
theQPainter->fillRect( static_cast<int>( theRasterViewPort->topLeftPoint.x() + 0.5 ),
|
|
static_cast<int>( theRasterViewPort->topLeftPoint.y() + 0.5 ),
|
|
static_cast<int>( theRasterViewPort->bottomRightPoint.x() ),
|
|
static_cast<int>( theRasterViewPort->bottomRightPoint.y() ),
|
|
QBrush( myColor ) );
|
|
}
|
|
//1x2, 2x1 or 2x2
|
|
else if ( 2 >= theRasterViewPort->clippedWidth && 2 >= theRasterViewPort->clippedHeight )
|
|
{
|
|
int myPixelBoundaryX = 0;
|
|
int myPixelBoundaryY = 0;
|
|
if ( theQgsMapToPixel )
|
|
{
|
|
myPixelBoundaryX = static_cast<int>( theRasterViewPort->topLeftPoint.x() + 0.5 ) + static_cast<int>( qAbs( mGeoTransform[1] / theQgsMapToPixel->mapUnitsPerPixel() ) ) - paintXoffset;
|
|
myPixelBoundaryY = static_cast<int>( theRasterViewPort->topLeftPoint.y() + 0.5 ) + static_cast<int>( qAbs( mGeoTransform[5] / theQgsMapToPixel->mapUnitsPerPixel() ) ) - paintYoffset;
|
|
}
|
|
|
|
//INSTANCE: 1x2
|
|
if ( 1 == theRasterViewPort->clippedWidth )
|
|
{
|
|
QColor myColor( theImage->pixel( 0, 0 ) );
|
|
myColor.setAlpha( qAlpha( theImage->pixel( 0, 0 ) ) );
|
|
theQPainter->fillRect( static_cast<int>( theRasterViewPort->topLeftPoint.x() + 0.5 ),
|
|
static_cast<int>( theRasterViewPort->topLeftPoint.y() + 0.5 ),
|
|
static_cast<int>( theRasterViewPort->bottomRightPoint.x() ),
|
|
static_cast<int>( myPixelBoundaryY ),
|
|
QBrush( myColor ) );
|
|
myColor = QColor( theImage->pixel( 0, 1 ) );
|
|
myColor.setAlpha( qAlpha( theImage->pixel( 0, 1 ) ) );
|
|
theQPainter->fillRect( static_cast<int>( theRasterViewPort->topLeftPoint.x() + 0.5 ),
|
|
static_cast<int>( myPixelBoundaryY ),
|
|
static_cast<int>( theRasterViewPort->bottomRightPoint.x() ),
|
|
static_cast<int>( theRasterViewPort->bottomRightPoint.y() ),
|
|
QBrush( myColor ) );
|
|
}
|
|
else
|
|
{
|
|
//INSTANCE: 2x1
|
|
if ( 1 == theRasterViewPort->clippedHeight )
|
|
{
|
|
QColor myColor( theImage->pixel( 0, 0 ) );
|
|
myColor.setAlpha( qAlpha( theImage->pixel( 0, 0 ) ) );
|
|
theQPainter->fillRect( static_cast<int>( theRasterViewPort->topLeftPoint.x() + 0.5 ),
|
|
static_cast<int>( theRasterViewPort->topLeftPoint.y() + 0.5 ),
|
|
static_cast<int>( myPixelBoundaryX ),
|
|
static_cast<int>( theRasterViewPort->bottomRightPoint.y() ),
|
|
QBrush( myColor ) );
|
|
myColor = QColor( theImage->pixel( 1, 0 ) );
|
|
myColor.setAlpha( qAlpha( theImage->pixel( 1, 0 ) ) );
|
|
theQPainter->fillRect( static_cast<int>( myPixelBoundaryX ),
|
|
static_cast<int>( theRasterViewPort->topLeftPoint.y() + 0.5 ),
|
|
static_cast<int>( theRasterViewPort->bottomRightPoint.x() ),
|
|
static_cast<int>( theRasterViewPort->bottomRightPoint.y() ),
|
|
QBrush( myColor ) );
|
|
}
|
|
//INSTANCE: 2x2
|
|
else
|
|
{
|
|
QColor myColor( theImage->pixel( 0, 0 ) );
|
|
myColor.setAlpha( qAlpha( theImage->pixel( 0, 0 ) ) );
|
|
theQPainter->fillRect( static_cast<int>( theRasterViewPort->topLeftPoint.x() + 0.5 ),
|
|
static_cast<int>( theRasterViewPort->topLeftPoint.y() + 0.5 ),
|
|
static_cast<int>( myPixelBoundaryX ),
|
|
static_cast<int>( myPixelBoundaryY ),
|
|
QBrush( myColor ) );
|
|
myColor = QColor( theImage->pixel( 1, 0 ) );
|
|
myColor.setAlpha( qAlpha( theImage->pixel( 1, 0 ) ) );
|
|
theQPainter->fillRect( static_cast<int>( myPixelBoundaryX ),
|
|
static_cast<int>( theRasterViewPort->topLeftPoint.y() + 0.5 ),
|
|
static_cast<int>( theRasterViewPort->bottomRightPoint.x() ),
|
|
static_cast<int>( myPixelBoundaryY ),
|
|
QBrush( myColor ) );
|
|
myColor = QColor( theImage->pixel( 0, 1 ) );
|
|
myColor.setAlpha( qAlpha( theImage->pixel( 0, 1 ) ) );
|
|
theQPainter->fillRect( static_cast<int>( theRasterViewPort->topLeftPoint.x() + 0.5 ),
|
|
static_cast<int>( myPixelBoundaryY ),
|
|
static_cast<int>( myPixelBoundaryX ),
|
|
static_cast<int>( theRasterViewPort->bottomRightPoint.y() ),
|
|
QBrush( myColor ) );
|
|
myColor = QColor( theImage->pixel( 1, 1 ) );
|
|
myColor.setAlpha( qAlpha( theImage->pixel( 1, 1 ) ) );
|
|
theQPainter->fillRect( static_cast<int>( myPixelBoundaryX ),
|
|
static_cast<int>( myPixelBoundaryY ),
|
|
static_cast<int>( theRasterViewPort->bottomRightPoint.x() ),
|
|
static_cast<int>( theRasterViewPort->bottomRightPoint.y() ),
|
|
QBrush( myColor ) );
|
|
}
|
|
}
|
|
|
|
}
|
|
// INSTANCE: > 2x2, so just use the image filled by GDAL
|
|
else
|
|
{
|
|
theQPainter->drawImage( static_cast<int>( theRasterViewPort->topLeftPoint.x() + 0.5 ),
|
|
static_cast<int>( theRasterViewPort->topLeftPoint.y() + 0.5 ),
|
|
*theImage,
|
|
paintXoffset,
|
|
paintYoffset );
|
|
}
|
|
}
|
|
|
|
QString QgsRasterLayer::projectionWkt()
|
|
{
|
|
QString myWktString;
|
|
QgsCoordinateReferenceSystem myCRS;
|
|
myWktString = QString( GDALGetProjectionRef( mGdalDataset ) );
|
|
myCRS.createFromWkt( myWktString );
|
|
if ( !myCRS.isValid() )
|
|
{
|
|
//try to get the gcp srs from the raster layer if available
|
|
myWktString = QString( GDALGetGCPProjection( mGdalDataset ) );
|
|
|
|
// What is the purpose of this piece of code?
|
|
// Sideeffects from validate()?
|
|
// myCRS.createFromWkt(myWktString);
|
|
// if (!myCRS.isValid())
|
|
// {
|
|
// // use force and make CRS valid!
|
|
// myCRS.validate();
|
|
// }
|
|
|
|
}
|
|
|
|
return myWktString;
|
|
}
|
|
|
|
/*
|
|
*data type is the same as raster band. The memory must be released later!
|
|
* \return pointer to the memory
|
|
*/
|
|
void *QgsRasterLayer::readData( GDALRasterBandH gdalBand, QgsRasterViewPort *viewPort )
|
|
{
|
|
GDALDataType type = GDALGetRasterDataType( gdalBand );
|
|
int size = GDALGetDataTypeSize( type ) / 8;
|
|
|
|
QgsDebugMsg( "calling RasterIO with " +
|
|
QString( ", source NW corner: " ) + QString::number( viewPort->rectXOffset ) +
|
|
", " + QString::number( viewPort->rectYOffset ) +
|
|
", source size: " + QString::number( viewPort->clippedWidth ) +
|
|
", " + QString::number( viewPort->clippedHeight ) +
|
|
", dest size: " + QString::number( viewPort->drawableAreaXDim ) +
|
|
", " + QString::number( viewPort->drawableAreaYDim ) );
|
|
|
|
void *data = VSIMalloc( size * viewPort->drawableAreaXDim * viewPort->drawableAreaYDim );
|
|
|
|
/* Abort if out of memory */
|
|
if ( data == NULL )
|
|
{
|
|
QgsDebugMsg( "Layer " + name() + " couldn't allocate enough memory. Ignoring" );
|
|
}
|
|
else
|
|
{
|
|
CPLErr myErr = GDALRasterIO( gdalBand, GF_Read,
|
|
viewPort->rectXOffset,
|
|
viewPort->rectYOffset,
|
|
viewPort->clippedWidth,
|
|
viewPort->clippedHeight,
|
|
data,
|
|
viewPort->drawableAreaXDim,
|
|
viewPort->drawableAreaYDim,
|
|
type, 0, 0 );
|
|
if ( myErr != CPLE_None )
|
|
{
|
|
QgsLogger::warning( "RasterIO error: " + QString::fromUtf8( CPLGetLastErrorMsg() ) );
|
|
}
|
|
}
|
|
return data;
|
|
}
|
|
|
|
/*
|
|
* @note Called from ctor if a raster image given there
|
|
*
|
|
* @param theFilename absolute path and filename of the raster to be loaded
|
|
* @returns true if successfully read file
|
|
*/
|
|
bool QgsRasterLayer::readFile( QString const &theFilename )
|
|
{
|
|
registerGdalDrivers();
|
|
|
|
mGdalDataset = NULL;
|
|
|
|
//open the dataset making sure we handle char encoding of locale properly
|
|
mGdalBaseDataset = GDALOpen( TO8F( theFilename ), GA_ReadOnly );
|
|
|
|
if ( mGdalBaseDataset == NULL )
|
|
{
|
|
mValid = false;
|
|
return false;
|
|
}
|
|
|
|
// Store timestamp
|
|
mLastModified = lastModified( theFilename );
|
|
|
|
// Check if we need a warped VRT for this file.
|
|
if (( GDALGetGeoTransform( mGdalBaseDataset, mGeoTransform ) == CE_None
|
|
&& ( mGeoTransform[1] < 0.0
|
|
|| mGeoTransform[2] != 0.0
|
|
|| mGeoTransform[4] != 0.0
|
|
|| mGeoTransform[5] > 0.0 ) )
|
|
|| GDALGetGCPCount( mGdalBaseDataset ) > 0 )
|
|
{
|
|
QgsLogger::warning( "Creating Warped VRT." );
|
|
|
|
mGdalDataset =
|
|
GDALAutoCreateWarpedVRT( mGdalBaseDataset, NULL, NULL,
|
|
GRA_NearestNeighbour, 0.2, NULL );
|
|
if ( mGdalDataset == NULL )
|
|
{
|
|
QgsLogger::warning( "Warped VRT Creation failed." );
|
|
mGdalDataset = mGdalBaseDataset;
|
|
GDALReferenceDataset( mGdalDataset );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mGdalDataset = mGdalBaseDataset;
|
|
GDALReferenceDataset( mGdalDataset );
|
|
}
|
|
|
|
if ( subLayers().size() > 0 )
|
|
{
|
|
// just to get the sublayers
|
|
mValid = false;
|
|
return true;
|
|
}
|
|
|
|
//check f this file has pyramids
|
|
GDALRasterBandH myGDALBand = GDALGetRasterBand( mGdalDataset, 1 ); //just use the first band
|
|
if ( myGDALBand == NULL )
|
|
{
|
|
GDALDereferenceDataset( mGdalBaseDataset );
|
|
mGdalBaseDataset = NULL;
|
|
|
|
GDALClose( mGdalDataset );
|
|
mGdalDataset = NULL;
|
|
mValid = false;
|
|
return false;
|
|
}
|
|
|
|
mHasPyramids = GDALGetOverviewCount( myGDALBand ) > 0;
|
|
|
|
//populate the list of what pyramids exist
|
|
buildPyramidList();
|
|
|
|
// Get the layer's projection info and set up the
|
|
// QgsCoordinateTransform for this layer
|
|
// NOTE: we must do this before metadata is called
|
|
|
|
QString mySourceWkt = projectionWkt();
|
|
|
|
QgsDebugMsg( "--------------------------------------------------------------------------------------" );
|
|
QgsDebugMsg( "using wkt:\n" + mySourceWkt );
|
|
QgsDebugMsg( "--------------------------------------------------------------------------------------" );
|
|
|
|
mCRS->createFromWkt( mySourceWkt );
|
|
//get the project projection, defaulting to this layer's projection
|
|
//if none exists....
|
|
if ( !mCRS->isValid() )
|
|
{
|
|
mCRS->setValidationHint( tr( "Specify CRS for layer %1" ).arg( name() ) );
|
|
mCRS->validate();
|
|
}
|
|
|
|
//set up the coordinat transform - in the case of raster this is mainly used to convert
|
|
//the inverese projection of the map extents of the canvas when zzooming in etc. so
|
|
//that they match the coordinate system of this layer
|
|
QgsDebugMsg( "Layer registry has " + QString::number( QgsMapLayerRegistry::instance()->count() ) + "layers" );
|
|
|
|
metadata();
|
|
|
|
// Use the affine transform to get geo coordinates for
|
|
// the corners of the raster
|
|
double myXMax = mGeoTransform[0] +
|
|
GDALGetRasterXSize( mGdalDataset ) * mGeoTransform[1] +
|
|
GDALGetRasterYSize( mGdalDataset ) * mGeoTransform[2];
|
|
double myYMin = mGeoTransform[3] +
|
|
GDALGetRasterXSize( mGdalDataset ) * mGeoTransform[4] +
|
|
GDALGetRasterYSize( mGdalDataset ) * mGeoTransform[5];
|
|
|
|
mLayerExtent.setXMaximum( myXMax );
|
|
// The affine transform reduces to these values at the
|
|
// top-left corner of the raster
|
|
mLayerExtent.setXMinimum( mGeoTransform[0] );
|
|
mLayerExtent.setYMaximum( mGeoTransform[3] );
|
|
mLayerExtent.setYMinimum( myYMin );
|
|
|
|
//
|
|
// Set up the x and y dimensions of this raster layer
|
|
//
|
|
mWidth = GDALGetRasterXSize( mGdalDataset );
|
|
mHeight = GDALGetRasterYSize( mGdalDataset );
|
|
|
|
//
|
|
// Determine the nodata value
|
|
//
|
|
mNoDataValue = -9999.0; //Standard default?
|
|
mValidNoDataValue = false;
|
|
int isValid = false;
|
|
double myNoDataValue = GDALGetRasterNoDataValue( GDALGetRasterBand( mGdalDataset, 1 ), &isValid );
|
|
if ( isValid )
|
|
{
|
|
mNoDataValue = myNoDataValue;
|
|
mValidNoDataValue = true;
|
|
}
|
|
|
|
if ( mValidNoDataValue )
|
|
{
|
|
mRasterTransparency.initializeTransparentPixelList( mNoDataValue, mNoDataValue, mNoDataValue );
|
|
mRasterTransparency.initializeTransparentPixelList( mNoDataValue );
|
|
}
|
|
|
|
mBandCount = GDALGetRasterCount( mGdalDataset );
|
|
for ( int i = 1; i <= mBandCount; i++ )
|
|
{
|
|
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, i );
|
|
QgsRasterBandStats myRasterBandStats;
|
|
myRasterBandStats.bandName = generateBandName( i );
|
|
myRasterBandStats.bandNumber = i;
|
|
myRasterBandStats.statsGathered = false;
|
|
myRasterBandStats.histogramVector = new QgsRasterBandStats::HistogramVector();
|
|
//Store the default color table
|
|
readColorTable( i, &myRasterBandStats.colorTable );
|
|
|
|
mRasterStatsList.push_back( myRasterBandStats );
|
|
|
|
//Build a new contrast enhancement for the band and store in list
|
|
QgsContrastEnhancement myContrastEnhancement(( QgsContrastEnhancement::QgsRasterDataType )GDALGetRasterDataType( myGdalBand ) );
|
|
mContrastEnhancementList.append( myContrastEnhancement );
|
|
}
|
|
|
|
//defaults - Needs to be set after the Contrast list has been build
|
|
//Try to read the default contrast enhancement from the config file
|
|
QSettings myQSettings;
|
|
setContrastEnhancementAlgorithm( myQSettings.value( "/Raster/defaultContrastEnhancementAlgorithm", "StretchToMinimumMaximum" ).toString() );
|
|
|
|
//decide what type of layer this is...
|
|
//TODO Change this to look at the color interp and palette interp to decide which type of layer it is
|
|
if (( GDALGetRasterCount( mGdalDataset ) > 1 ) )
|
|
{
|
|
mRasterType = Multiband;
|
|
}
|
|
//TODO hasBand is really obsolete and only used in the Palette instance, change to new function hasPalette(int)
|
|
else if ( hasBand( "Palette" ) ) //don't tr() this its a gdal word!
|
|
{
|
|
mRasterType = Palette;
|
|
}
|
|
else
|
|
{
|
|
mRasterType = GrayOrUndefined;
|
|
}
|
|
|
|
if ( mRasterType == Palette )
|
|
{
|
|
mRedBandName = TRSTRING_NOT_SET; // sensible default
|
|
mGreenBandName = TRSTRING_NOT_SET; // sensible default
|
|
mBlueBandName = TRSTRING_NOT_SET;// sensible default
|
|
mTransparencyBandName = TRSTRING_NOT_SET; // sensible default
|
|
mGrayBandName = bandName( 1 ); //sensible default
|
|
QgsDebugMsg( mGrayBandName );
|
|
|
|
mDrawingStyle = PalettedColor; //sensible default
|
|
|
|
//Set up a new color ramp shader
|
|
setColorShadingAlgorithm( ColorRampShader );
|
|
QgsColorRampShader* myColorRampShader = ( QgsColorRampShader* ) mRasterShader->rasterShaderFunction();
|
|
//TODO: Make sure the set algorithm and cast was successful,
|
|
//e.g., if ( 0 != myColorRampShader && myColorRampShader->shaderTypeAsString == "ColorRampShader" )
|
|
myColorRampShader->setColorRampType( QgsColorRampShader::INTERPOLATED );
|
|
myColorRampShader->setColorRampItemList( *colorTable( 1 ) );
|
|
}
|
|
else if ( mRasterType == Multiband )
|
|
{
|
|
//we know we have at least 2 layers...
|
|
mRedBandName = bandName( myQSettings.value( "/Raster/defaultRedBand", 1 ).toInt() ); // sensible default
|
|
mGreenBandName = bandName( myQSettings.value( "/Raster/defaultGreenBand", 2 ).toInt() ); // sensible default
|
|
|
|
//Check to make sure preferred bands combinations are valid
|
|
if ( mRedBandName.isEmpty() )
|
|
{
|
|
mRedBandName = bandName( 1 );
|
|
}
|
|
|
|
if ( mGreenBandName.isEmpty() )
|
|
{
|
|
mGreenBandName = bandName( 2 );
|
|
}
|
|
|
|
//for the third layer we cant be sure so..
|
|
if ( GDALGetRasterCount( mGdalDataset ) > 2 )
|
|
{
|
|
mBlueBandName = bandName( myQSettings.value( "/Raster/defaultBlueBand", 3 ).toInt() ); // sensible default
|
|
if ( mBlueBandName.isEmpty() )
|
|
{
|
|
mBlueBandName = bandName( 3 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mBlueBandName = bandName( myQSettings.value( "/Raster/defaultBlueBand", 2 ).toInt() ); // sensible default
|
|
if ( mBlueBandName.isEmpty() )
|
|
{
|
|
mBlueBandName = bandName( 2 );
|
|
}
|
|
}
|
|
|
|
|
|
mTransparencyBandName = TRSTRING_NOT_SET;
|
|
mGrayBandName = TRSTRING_NOT_SET; //sensible default
|
|
mDrawingStyle = MultiBandColor; //sensible default
|
|
|
|
// read standard deviations
|
|
if ( mContrastEnhancementAlgorithm == QgsContrastEnhancement::StretchToMinimumMaximum )
|
|
{
|
|
setStandardDeviations( myQSettings.value( "/Raster/defaultStandardDeviation", 2 ).toInt() );
|
|
}
|
|
}
|
|
else //GrayOrUndefined
|
|
{
|
|
mRedBandName = TRSTRING_NOT_SET; //sensible default
|
|
mGreenBandName = TRSTRING_NOT_SET; //sensible default
|
|
mBlueBandName = TRSTRING_NOT_SET; //sensible default
|
|
mTransparencyBandName = TRSTRING_NOT_SET; //sensible default
|
|
mDrawingStyle = SingleBandGray; //sensible default
|
|
mGrayBandName = bandName( 1 );
|
|
|
|
// read standard deviations
|
|
if ( mContrastEnhancementAlgorithm == QgsContrastEnhancement::StretchToMinimumMaximum )
|
|
{
|
|
setStandardDeviations( myQSettings.value( "/Raster/defaultStandardDeviation", 2 ).toInt() );
|
|
}
|
|
}
|
|
|
|
//mark the layer as valid
|
|
mValid = true;
|
|
return true;
|
|
|
|
} // QgsRasterLayer::readFile
|
|
|
|
/*
|
|
* @param index index in memory block
|
|
*/
|
|
double QgsRasterLayer::readValue( void *data, GDALDataType type, int index )
|
|
{
|
|
if ( !data )
|
|
return mValidNoDataValue ? mNoDataValue : 0.0;
|
|
|
|
switch ( type )
|
|
{
|
|
case GDT_Byte:
|
|
return ( double )(( GByte * )data )[index];
|
|
break;
|
|
case GDT_UInt16:
|
|
return ( double )(( GUInt16 * )data )[index];
|
|
break;
|
|
case GDT_Int16:
|
|
return ( double )(( GInt16 * )data )[index];
|
|
break;
|
|
case GDT_UInt32:
|
|
return ( double )(( GUInt32 * )data )[index];
|
|
break;
|
|
case GDT_Int32:
|
|
return ( double )(( GInt32 * )data )[index];
|
|
break;
|
|
case GDT_Float32:
|
|
return ( double )(( float * )data )[index];
|
|
break;
|
|
case GDT_Float64:
|
|
return ( double )(( double * )data )[index];
|
|
break;
|
|
default:
|
|
QgsLogger::warning( "GDAL data type is not supported" );
|
|
}
|
|
|
|
return mValidNoDataValue ? mNoDataValue : 0.0;
|
|
}
|
|
|
|
bool QgsRasterLayer::update()
|
|
{
|
|
QgsDebugMsg( "entered." );
|
|
|
|
if ( mLastModified < QgsRasterLayer::lastModified( source() ) )
|
|
{
|
|
if ( !usesProvider() )
|
|
{
|
|
QgsDebugMsg( "Outdated -> reload" );
|
|
closeDataset();
|
|
return readFile( source() );
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool QgsRasterLayer::usesProvider()
|
|
{
|
|
return !mProviderKey.isEmpty();
|
|
}
|
|
|
|
QString QgsRasterLayer::validateBandName( QString const & theBandName )
|
|
{
|
|
QgsDebugMsg( "Checking..." );
|
|
//check if the band is unset
|
|
if ( theBandName == TRSTRING_NOT_SET || theBandName == QSTRING_NOT_SET )
|
|
{
|
|
QgsDebugMsg( "Band name is '" + QSTRING_NOT_SET + "'. Nothing to do." );
|
|
// Use translated name internally
|
|
return TRSTRING_NOT_SET;
|
|
}
|
|
|
|
//check that a valid band name was passed
|
|
QgsDebugMsg( "Looking through raster band stats for matching band name" );
|
|
for ( int myIterator = 0; myIterator < mRasterStatsList.size(); ++myIterator )
|
|
{
|
|
//find out the name of this band
|
|
if ( mRasterStatsList[myIterator].bandName == theBandName )
|
|
{
|
|
QgsDebugMsg( "Matching band name found" );
|
|
return theBandName;
|
|
}
|
|
}
|
|
QgsDebugMsg( "No matching band name found in raster band stats" );
|
|
|
|
QgsDebugMsg( "Testing for non zero-buffered names" );
|
|
//TODO Remove test in v2.0 or earlier
|
|
QStringList myBandNameComponents = theBandName.split( " " );
|
|
if ( myBandNameComponents.size() == 2 )
|
|
{
|
|
int myBandNumber = myBandNameComponents.at( 1 ).toInt();
|
|
if ( myBandNumber > 0 )
|
|
{
|
|
QString myBandName = generateBandName( myBandNumber );
|
|
for ( int myIterator = 0; myIterator < mRasterStatsList.size(); ++myIterator )
|
|
{
|
|
//find out the name of this band
|
|
if ( mRasterStatsList[myIterator].bandName == myBandName )
|
|
{
|
|
QgsDebugMsg( "Matching band name found" );
|
|
return myBandName;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
QgsDebugMsg( "Testing older naming format" );
|
|
//See of the band in an older format #:something.
|
|
//TODO Remove test in v2.0 or earlier
|
|
myBandNameComponents.clear();
|
|
if ( theBandName.contains( ':' ) )
|
|
{
|
|
myBandNameComponents = theBandName.split( ":" );
|
|
if ( myBandNameComponents.size() == 2 )
|
|
{
|
|
int myBandNumber = myBandNameComponents.at( 0 ).toInt();
|
|
if ( myBandNumber > 0 )
|
|
{
|
|
QgsDebugMsg( "Transformed older name format to current format" );
|
|
return "Band " + QString::number( myBandNumber );
|
|
}
|
|
}
|
|
}
|
|
|
|
//if no matches were found default to not set
|
|
QgsDebugMsg( "All checks failed, returning '" + QSTRING_NOT_SET + "'" );
|
|
return TRSTRING_NOT_SET;
|
|
}
|
|
|
|
QgsRasterImageBuffer::QgsRasterImageBuffer( GDALRasterBandH rasterBand, QPainter* p, QgsRasterViewPort* viewPort, const QgsMapToPixel* mapToPixel, double* geoTransform ):
|
|
mRasterBand( rasterBand ), mPainter( p ), mViewPort( viewPort ), mMapToPixel( mapToPixel ), mGeoTransform( geoTransform ), mValid( false ), mWritingEnabled( true ), mDrawPixelRect( false ), mCurrentImage( 0 ), mCurrentGDALData( 0 )
|
|
{
|
|
|
|
}
|
|
|
|
QgsRasterImageBuffer::~QgsRasterImageBuffer()
|
|
{
|
|
delete mCurrentImage;
|
|
CPLFree( mCurrentGDALData );
|
|
}
|
|
|
|
void QgsRasterImageBuffer::reset( int maxPixelsInVirtualMemory )
|
|
{
|
|
if ( !mRasterBand || !mPainter || !mViewPort )
|
|
{
|
|
mValid = false;
|
|
return;
|
|
}
|
|
|
|
mValid = true;
|
|
|
|
//decide on the partition of the image
|
|
|
|
int pixels = mViewPort->drawableAreaXDim * mViewPort->drawableAreaYDim;
|
|
int mNumPartImages = pixels / maxPixelsInVirtualMemory + 1.0;
|
|
mNumRasterRowsPerPart = ( double )mViewPort->clippedHeight / ( double )mNumPartImages + 0.5;
|
|
|
|
mCurrentPartRasterMin = -1;
|
|
mCurrentPartRasterMax = -1;
|
|
mCurrentPartImageRow = 0;
|
|
mNumCurrentImageRows = 0;
|
|
|
|
createNextPartImage();
|
|
|
|
if ( 2 >= mViewPort->clippedWidth && 2 >= mViewPort->clippedHeight )
|
|
{
|
|
//use Peter's fix for zoomed in rasters
|
|
mDrawPixelRect = true;
|
|
}
|
|
}
|
|
|
|
bool QgsRasterImageBuffer::nextScanLine( QRgb** imageScanLine, void** rasterScanLine )
|
|
{
|
|
if ( !mValid )
|
|
return false;
|
|
|
|
if ( !mCurrentImage && !mCurrentGDALData )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( mCurrentPartImageRow >= mNumCurrentImageRows )
|
|
{
|
|
if ( !createNextPartImage() )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if ( mWritingEnabled )
|
|
{
|
|
*imageScanLine = ( QRgb* ) mCurrentImage->scanLine( mCurrentPartImageRow );
|
|
}
|
|
else
|
|
{
|
|
*imageScanLine = 0;
|
|
}
|
|
GDALDataType type = GDALGetRasterDataType( mRasterBand );
|
|
int size = GDALGetDataTypeSize( type ) / 8;
|
|
*rasterScanLine = ( unsigned char * )mCurrentGDALData + mCurrentPartImageRow * mViewPort->drawableAreaXDim * size;
|
|
|
|
++mCurrentPartImageRow;
|
|
++mCurrentRow;
|
|
return !mWritingEnabled || *imageScanLine;
|
|
}
|
|
|
|
bool QgsRasterImageBuffer::createNextPartImage()
|
|
{
|
|
//draw the last image if mCurrentImage exists
|
|
if ( mCurrentImage )
|
|
{
|
|
if ( mWritingEnabled )
|
|
{
|
|
if ( 2 >= mViewPort->clippedWidth && 2 >= mViewPort->clippedHeight )
|
|
{
|
|
drawPixelRectangle();
|
|
}
|
|
else
|
|
{
|
|
int paintXoffset = 0;
|
|
int paintYoffset = 0;
|
|
int imageX = 0;
|
|
int imageY = 0;
|
|
|
|
if ( mMapToPixel )
|
|
{
|
|
paintXoffset = static_cast<int>(
|
|
( mViewPort->rectXOffsetFloat -
|
|
mViewPort->rectXOffset )
|
|
/ mMapToPixel->mapUnitsPerPixel()
|
|
* qAbs( mGeoTransform[1] )
|
|
);
|
|
|
|
paintYoffset = static_cast<int>(
|
|
( mViewPort->rectYOffsetFloat -
|
|
mViewPort->rectYOffset )
|
|
/ mMapToPixel->mapUnitsPerPixel()
|
|
* qAbs( mGeoTransform[5] )
|
|
);
|
|
|
|
imageX = static_cast<int>( mViewPort->topLeftPoint.x() + 0.5 );
|
|
imageY = static_cast<int>( mViewPort->topLeftPoint.y() + 0.5 + qAbs( mGeoTransform[5] ) * mCurrentPartRasterMin / mMapToPixel->mapUnitsPerPixel() );
|
|
}
|
|
|
|
mPainter->drawImage( imageX,
|
|
imageY,
|
|
*mCurrentImage,
|
|
paintXoffset,
|
|
paintYoffset );
|
|
}
|
|
}
|
|
}
|
|
|
|
delete mCurrentImage; mCurrentImage = 0;
|
|
CPLFree( mCurrentGDALData ); mCurrentGDALData = 0;
|
|
|
|
if ( mCurrentPartRasterMax >= mViewPort->clippedHeight )
|
|
{
|
|
return false; //already at the end...
|
|
}
|
|
|
|
mCurrentPartRasterMin = mCurrentPartRasterMax + 1;
|
|
mCurrentPartRasterMax = mCurrentPartRasterMin + mNumRasterRowsPerPart;
|
|
if ( mCurrentPartRasterMax > mViewPort->clippedHeight )
|
|
{
|
|
mCurrentPartRasterMax = mViewPort->clippedHeight;
|
|
}
|
|
mCurrentRow = mCurrentPartRasterMin;
|
|
mCurrentPartImageRow = 0;
|
|
|
|
//read GDAL image data
|
|
GDALDataType type = GDALGetRasterDataType( mRasterBand );
|
|
int size = GDALGetDataTypeSize( type ) / 8;
|
|
int xSize = mViewPort->drawableAreaXDim;
|
|
int ySize = mViewPort->drawableAreaYDim;
|
|
|
|
//make the raster tiles overlap at least 2 pixels to avoid white stripes
|
|
int overlapRows = 0;
|
|
if ( mMapToPixel )
|
|
{
|
|
overlapRows = mMapToPixel->mapUnitsPerPixel() / qAbs( mGeoTransform[5] ) + 2;
|
|
}
|
|
if ( mCurrentPartRasterMax + overlapRows >= mViewPort->clippedHeight )
|
|
{
|
|
overlapRows = 0;
|
|
}
|
|
int rasterYSize = mCurrentPartRasterMax - mCurrentPartRasterMin + overlapRows;
|
|
|
|
if ( 2 >= mViewPort->clippedWidth && 2 >= mViewPort->clippedHeight ) //for zoomed in rasters
|
|
{
|
|
rasterYSize = mViewPort->clippedHeight;
|
|
ySize = mViewPort->drawableAreaYDim;
|
|
}
|
|
else //normal mode
|
|
{
|
|
if ( mMapToPixel )
|
|
{
|
|
ySize = qAbs((( rasterYSize ) / mMapToPixel->mapUnitsPerPixel() * mGeoTransform[5] ) ) + 0.5;
|
|
}
|
|
}
|
|
if ( ySize < 1 || xSize < 1 )
|
|
{
|
|
return false;
|
|
}
|
|
mNumCurrentImageRows = ySize;
|
|
mCurrentGDALData = VSIMalloc( size * xSize * ySize );
|
|
CPLErr myErr = GDALRasterIO( mRasterBand, GF_Read, mViewPort->rectXOffset,
|
|
mViewPort->rectYOffset + mCurrentRow, mViewPort->clippedWidth, rasterYSize,
|
|
mCurrentGDALData, xSize, ySize, type, 0, 0 );
|
|
|
|
if ( myErr != CPLE_None )
|
|
{
|
|
CPLFree( mCurrentGDALData );
|
|
mCurrentGDALData = 0;
|
|
return false;
|
|
}
|
|
|
|
//create the QImage
|
|
if ( mWritingEnabled )
|
|
{
|
|
mCurrentImage = new QImage( xSize, ySize, QImage::Format_ARGB32 );
|
|
mCurrentImage->fill( qRgba( 255, 255, 255, 0 ) );
|
|
}
|
|
else
|
|
{
|
|
mCurrentImage = 0;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void QgsRasterImageBuffer::drawPixelRectangle()
|
|
{
|
|
// Set up the initial offset into the myQImage we want to copy to the map canvas
|
|
// This is useful when the source image pixels are larger than the screen image.
|
|
int paintXoffset = 0;
|
|
int paintYoffset = 0;
|
|
|
|
if ( mMapToPixel )
|
|
{
|
|
paintXoffset = static_cast<int>(
|
|
( mViewPort->rectXOffsetFloat -
|
|
mViewPort->rectXOffset )
|
|
/ mMapToPixel->mapUnitsPerPixel()
|
|
* qAbs( mGeoTransform[1] )
|
|
);
|
|
|
|
paintYoffset = static_cast<int>(
|
|
( mViewPort->rectYOffsetFloat -
|
|
mViewPort->rectYOffset )
|
|
/ mMapToPixel->mapUnitsPerPixel()
|
|
* qAbs( mGeoTransform[5] )
|
|
);
|
|
}
|
|
|
|
//fix for zoomed in rasters
|
|
//Catch special rendering cases
|
|
//INSTANCE: 1x1
|
|
if ( 1 == mViewPort->clippedWidth && 1 == mViewPort->clippedHeight )
|
|
{
|
|
QColor myColor( mCurrentImage->pixel( 0, 0 ) );
|
|
myColor.setAlpha( qAlpha( mCurrentImage->pixel( 0, 0 ) ) );
|
|
mPainter->fillRect( static_cast<int>( mViewPort->topLeftPoint.x() + 0.5 ),
|
|
static_cast<int>( mViewPort->topLeftPoint.y() + 0.5 ),
|
|
static_cast<int>( mViewPort->bottomRightPoint.x() ),
|
|
static_cast<int>( mViewPort->bottomRightPoint.y() ),
|
|
QBrush( myColor ) );
|
|
}
|
|
//1x2, 2x1 or 2x2
|
|
else if ( 2 >= mViewPort->clippedWidth && 2 >= mViewPort->clippedHeight )
|
|
{
|
|
int myPixelBoundaryX = 0;
|
|
int myPixelBoundaryY = 0;
|
|
if ( mMapToPixel )
|
|
{
|
|
myPixelBoundaryX = static_cast<int>( mViewPort->topLeftPoint.x() + 0.5 ) + static_cast<int>( qAbs( mGeoTransform[1] / mMapToPixel->mapUnitsPerPixel() ) ) - paintXoffset;
|
|
myPixelBoundaryY = static_cast<int>( mViewPort->topLeftPoint.y() + 0.5 ) + static_cast<int>( qAbs( mGeoTransform[5] / mMapToPixel->mapUnitsPerPixel() ) ) - paintYoffset;
|
|
}
|
|
|
|
//INSTANCE: 1x2
|
|
if ( 1 == mViewPort->clippedWidth )
|
|
{
|
|
QColor myColor( mCurrentImage->pixel( 0, 0 ) );
|
|
myColor.setAlpha( qAlpha( mCurrentImage->pixel( 0, 0 ) ) );
|
|
mPainter->fillRect( static_cast<int>( mViewPort->topLeftPoint.x() + 0.5 ),
|
|
static_cast<int>( mViewPort->topLeftPoint.y() + 0.5 ),
|
|
static_cast<int>( mViewPort->bottomRightPoint.x() ),
|
|
static_cast<int>( myPixelBoundaryY ),
|
|
QBrush( myColor ) );
|
|
myColor = QColor( mCurrentImage->pixel( 0, 1 ) );
|
|
myColor.setAlpha( qAlpha( mCurrentImage->pixel( 0, 1 ) ) );
|
|
mPainter->fillRect( static_cast<int>( mViewPort->topLeftPoint.x() + 0.5 ),
|
|
static_cast<int>( myPixelBoundaryY ),
|
|
static_cast<int>( mViewPort->bottomRightPoint.x() ),
|
|
static_cast<int>( mViewPort->bottomRightPoint.y() ),
|
|
QBrush( myColor ) );
|
|
}
|
|
else
|
|
{
|
|
//INSTANCE: 2x1
|
|
if ( 1 == mViewPort->clippedHeight )
|
|
{
|
|
QColor myColor( mCurrentImage->pixel( 0, 0 ) );
|
|
myColor.setAlpha( qAlpha( mCurrentImage->pixel( 0, 0 ) ) );
|
|
mPainter->fillRect( static_cast<int>( mViewPort->topLeftPoint.x() + 0.5 ),
|
|
static_cast<int>( mViewPort->topLeftPoint.y() + 0.5 ),
|
|
static_cast<int>( myPixelBoundaryX ),
|
|
static_cast<int>( mViewPort->bottomRightPoint.y() ),
|
|
QBrush( myColor ) );
|
|
myColor = QColor( mCurrentImage->pixel( 1, 0 ) );
|
|
myColor.setAlpha( qAlpha( mCurrentImage->pixel( 1, 0 ) ) );
|
|
mPainter->fillRect( static_cast<int>( myPixelBoundaryX ),
|
|
static_cast<int>( mViewPort->topLeftPoint.y() + 0.5 ),
|
|
static_cast<int>( mViewPort->bottomRightPoint.x() ),
|
|
static_cast<int>( mViewPort->bottomRightPoint.y() ),
|
|
QBrush( myColor ) );
|
|
}
|
|
//INSTANCE: 2x2
|
|
else
|
|
{
|
|
QColor myColor( mCurrentImage->pixel( 0, 0 ) );
|
|
myColor.setAlpha( qAlpha( mCurrentImage->pixel( 0, 0 ) ) );
|
|
mPainter->fillRect( static_cast<int>( mViewPort->topLeftPoint.x() + 0.5 ),
|
|
static_cast<int>( mViewPort->topLeftPoint.y() + 0.5 ),
|
|
static_cast<int>( myPixelBoundaryX ),
|
|
static_cast<int>( myPixelBoundaryY ),
|
|
QBrush( myColor ) );
|
|
myColor = QColor( mCurrentImage->pixel( 1, 0 ) );
|
|
myColor.setAlpha( qAlpha( mCurrentImage->pixel( 1, 0 ) ) );
|
|
mPainter->fillRect( static_cast<int>( myPixelBoundaryX ),
|
|
static_cast<int>( mViewPort->topLeftPoint.y() + 0.5 ),
|
|
static_cast<int>( mViewPort->bottomRightPoint.x() ),
|
|
static_cast<int>( myPixelBoundaryY ),
|
|
QBrush( myColor ) );
|
|
myColor = QColor( mCurrentImage->pixel( 0, 1 ) );
|
|
myColor.setAlpha( qAlpha( mCurrentImage->pixel( 0, 1 ) ) );
|
|
mPainter->fillRect( static_cast<int>( mViewPort->topLeftPoint.x() + 0.5 ),
|
|
static_cast<int>( myPixelBoundaryY ),
|
|
static_cast<int>( myPixelBoundaryX ),
|
|
static_cast<int>( mViewPort->bottomRightPoint.y() ),
|
|
QBrush( myColor ) );
|
|
myColor = QColor( mCurrentImage->pixel( 1, 1 ) );
|
|
myColor.setAlpha( qAlpha( mCurrentImage->pixel( 1, 1 ) ) );
|
|
mPainter->fillRect( static_cast<int>( myPixelBoundaryX ),
|
|
static_cast<int>( myPixelBoundaryY ),
|
|
static_cast<int>( mViewPort->bottomRightPoint.x() ),
|
|
static_cast<int>( mViewPort->bottomRightPoint.y() ),
|
|
QBrush( myColor ) );
|
|
}
|
|
}
|
|
}
|
|
}
|