273 changes: 0 additions & 273 deletions src/providers/gdal/qgsgdaldataitems.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@
#include "qgsgdaldataitems.h"
#include "qgsgdalprovider.h"
#include "qgslogger.h"
#include "qgsdatasourceuri.h"
#include "qgswcssourceselect.h"
#include "qgsowsconnection.h"
#include "qgsnewhttpconnection.h"

#include <QFileInfo>
#include <QSettings>
Expand Down Expand Up @@ -128,259 +124,6 @@ QString QgsGdalLayerItem::layerName() const
return info.completeBaseName();
}

// ---------------------------------------------------------------------------
QgsWCSConnectionItem::QgsWCSConnectionItem( QgsDataItem* parent, QString name, QString path )
: QgsDataCollectionItem( parent, name, path )
{
mIcon = QIcon( QgsApplication::getThemePixmap( "mIconWcs.png" ) );
}

QgsWCSConnectionItem::~QgsWCSConnectionItem()
{
QgsDebugMsg( "Entered" );
}

QVector<QgsDataItem*> QgsWCSConnectionItem::createChildren()
{
QgsDebugMsg( "Entered" );
QVector<QgsDataItem*> children;

QString encodedUri = mPath;
QgsDataSourceURI uri;
uri.setEncodedUri( encodedUri );
QgsDebugMsg( "encodedUri = " + encodedUri );

mCapabilities.setUri( uri );

// Attention: supportedLayers() gives tree leafes, not top level
if ( !mCapabilities.lastError().isEmpty() )
{
//children.append( new QgsErrorItem( this, tr( "Failed to retrieve layers" ), mPath + "/error" ) );
// TODO: show the error without adding child
return children;
}

foreach ( QgsWcsCoverageSummary coverageSummary, mCapabilities.capabilities().contents.coverageSummary )
{
// Attention, the name may be empty
QgsDebugMsg( QString::number( coverageSummary.orderId ) + " " + coverageSummary.identifier + " " + coverageSummary.title );
QString pathName = coverageSummary.identifier.isEmpty() ? QString::number( coverageSummary.orderId ) : coverageSummary.identifier;

QgsWCSLayerItem * layer = new QgsWCSLayerItem( this, coverageSummary.title, mPath + "/" + pathName, mCapabilities.capabilities(), uri, coverageSummary );

children.append( layer );
}
return children;
}

bool QgsWCSConnectionItem::equal( const QgsDataItem *other )
{
if ( type() != other->type() )
{
return false;
}
const QgsWCSConnectionItem *o = dynamic_cast<const QgsWCSConnectionItem *>( other );
if ( !o )
{
return false;
}

return ( mPath == o->mPath && mName == o->mName );
}

QList<QAction*> QgsWCSConnectionItem::actions()
{
QList<QAction*> lst;

QAction* actionEdit = new QAction( tr( "Edit..." ), this );
connect( actionEdit, SIGNAL( triggered() ), this, SLOT( editConnection() ) );
lst.append( actionEdit );

QAction* actionDelete = new QAction( tr( "Delete" ), this );
connect( actionDelete, SIGNAL( triggered() ), this, SLOT( deleteConnection() ) );
lst.append( actionDelete );

return lst;
}

void QgsWCSConnectionItem::editConnection()
{
QgsNewHttpConnection nc( 0, "/Qgis/connections-wcs/", mName );

if ( nc.exec() )
{
// the parent should be updated
mParent->refresh();
}
}

void QgsWCSConnectionItem::deleteConnection()
{
QgsOWSConnection::deleteConnection( "WCS", mName );
// the parent should be updated
mParent->refresh();
}


// ---------------------------------------------------------------------------

QgsWCSLayerItem::QgsWCSLayerItem( QgsDataItem* parent, QString name, QString path, QgsWcsCapabilitiesProperty capabilitiesProperty, QgsDataSourceURI dataSourceUri, QgsWcsCoverageSummary coverageSummary )
: QgsLayerItem( parent, name, path, QString(), QgsLayerItem::Raster, "gdal" ),
mCapabilities( capabilitiesProperty ),
mDataSourceUri( dataSourceUri ),
mCoverageSummary( coverageSummary )
{
QgsDebugMsg( "uri = " + mDataSourceUri.encodedUri() );
mUri = createUri();
// Populate everything, it costs nothing, all info about layers is collected
foreach ( QgsWcsCoverageSummary coverageSummary, mCoverageSummary.coverageSummary )
{
// Attention, the name may be empty
QgsDebugMsg( QString::number( coverageSummary.orderId ) + " " + coverageSummary.identifier + " " + coverageSummary.title );
QString pathName = coverageSummary.identifier.isEmpty() ? QString::number( coverageSummary.orderId ) : coverageSummary.identifier;
QgsWCSLayerItem * layer = new QgsWCSLayerItem( this, coverageSummary.title, mPath + "/" + pathName, mCapabilities, mDataSourceUri, coverageSummary );
mChildren.append( layer );
}

if ( mChildren.size() == 0 )
{
//mIcon = iconRaster();
mIcon = QgsApplication::getThemeIcon( "mIconWcs.png" );
}
mPopulated = true;
}

QgsWCSLayerItem::~QgsWCSLayerItem()
{
}

QString QgsWCSLayerItem::createUri()
{
if ( mCoverageSummary.identifier.isEmpty() )
return ""; // layer collection

// Number of styles must match number of layers
mDataSourceUri.setParam( "identifier", mCoverageSummary.identifier );

// TODO(?): with WCS 1.0 GetCapabilities does not contain CRS and formats,
// to get them we would need to call QgsWcsCapabilities::describeCoverage
// but it is problematic to get QgsWcsCapabilities here (copy not allowed
// by QObject, pointer is dangerous (OWS provider is changing parent))
// We leave CRS and format default for now.

QString format;
// get first supported by GDAL and server
QStringList mimes = QgsGdalProvider::supportedMimes().keys();
// prefer tiff
if ( mimes.contains( "image/tiff" ) && mCoverageSummary.supportedFormat.contains( "image/tiff" ) )
{
format = "image/tiff";
}
else
{
foreach ( QString f, mimes )
{
if ( mCoverageSummary.supportedFormat.indexOf( f ) >= 0 )
{
format = f;
break;
}
}
}
if ( !format.isEmpty() )
{
mDataSourceUri.setParam( "format", format );
}

QString crs;

// TODO: prefer project CRS
// get first known if possible
QgsCoordinateReferenceSystem testCrs;
foreach ( QString c, mCoverageSummary.supportedCrs )
{
testCrs.createFromOgcWmsCrs( c );
if ( testCrs.isValid() )
{
crs = c;
break;
}
}
if ( crs.isEmpty() && mCoverageSummary.supportedCrs.size() > 0 )
{
crs = mCoverageSummary.supportedCrs.value( 0 );
}
if ( !crs.isEmpty() )
{
mDataSourceUri.setParam( "crs", crs );
}

return mDataSourceUri.encodedUri();
}

// ---------------------------------------------------------------------------

QgsWCSRootItem::QgsWCSRootItem( QgsDataItem* parent, QString name, QString path )
: QgsDataCollectionItem( parent, name, path )
{
mIcon = QgsApplication::getThemeIcon( "mIconWcs.png" );

populate();
}

QgsWCSRootItem::~QgsWCSRootItem()
{
}

QVector<QgsDataItem*>QgsWCSRootItem::createChildren()
{
QVector<QgsDataItem*> connections;
foreach ( QString connName, QgsOWSConnection::connectionList( "WCS" ) )
{
//QgsDataItem * conn = new QgsWCSConnectionItem( this, connName, mPath + "/" + connName );
QgsOWSConnection connection( "WCS", connName );
QgsDataItem * conn = new QgsWCSConnectionItem( this, connName, connection.uri().encodedUri() );

conn->setIcon( QgsApplication::getThemeIcon( "mIconConnect.png" ) );
connections.append( conn );
}
return connections;
}

QList<QAction*> QgsWCSRootItem::actions()
{
QList<QAction*> lst;

QAction* actionNew = new QAction( tr( "New Connection..." ), this );
connect( actionNew, SIGNAL( triggered() ), this, SLOT( newConnection() ) );
lst.append( actionNew );

return lst;
}


QWidget * QgsWCSRootItem::paramWidget()
{
QgsWCSSourceSelect *select = new QgsWCSSourceSelect( 0, 0, true, true );
connect( select, SIGNAL( connectionsChanged() ), this, SLOT( connectionsChanged() ) );
return select;
}

void QgsWCSRootItem::connectionsChanged()
{
refresh();
}

void QgsWCSRootItem::newConnection()
{
QgsNewHttpConnection nc( 0, "/Qgis/connections-wcs/" );

if ( nc.exec() )
{
refresh();
}
}

// ---------------------------------------------------------------------------

static QString filterString;
Expand All @@ -395,18 +138,6 @@ QGISEXTERN int dataCapabilities()
QGISEXTERN QgsDataItem * dataItem( QString thePath, QgsDataItem* parentItem )
{
QgsDebugMsg( "thePath = " + thePath );
if ( thePath.isEmpty() )
{
// Top level WCS
return new QgsWCSRootItem( parentItem, "WCS", "wcs:" );
}

if ( thePath.contains( "url=" ) )
{
// OWS server
QgsDebugMsg( "connection found in uri" );
return new QgsWCSConnectionItem( parentItem, "WCS", thePath );
}

// zip settings + info
QSettings settings;
Expand Down Expand Up @@ -534,7 +265,3 @@ QGISEXTERN QgsDataItem * dataItem( QString thePath, QgsDataItem* parentItem )
return item;
}

QGISEXTERN QgsWCSSourceSelect * selectWidget( QWidget * parent, Qt::WFlags fl )
{
return new QgsWCSSourceSelect( parent, fl );
}
59 changes: 0 additions & 59 deletions src/providers/gdal/qgsgdaldataitems.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@
#define QGSGDALDATAITEMS_H

#include "qgsdataitem.h"
#include "qgsdatasourceuri.h"
//#include "qgsowsconnection.h"
#include "qgswcscapabilities.h"

class QgsGdalLayerItem : public QgsLayerItem
{
Expand All @@ -40,60 +37,4 @@ class QgsGdalLayerItem : public QgsLayerItem
QString layerName() const;
};

class QgsWCSConnectionItem : public QgsDataCollectionItem
{
Q_OBJECT
public:
QgsWCSConnectionItem( QgsDataItem* parent, QString name, QString path );
~QgsWCSConnectionItem();

QVector<QgsDataItem*> createChildren();
virtual bool equal( const QgsDataItem *other );

virtual QList<QAction*> actions();

QgsWcsCapabilities mCapabilities;
QVector<QgsWcsCoverageSummary> mLayerProperties;

public slots:
void editConnection();
void deleteConnection();
};

// WCS Layers may be nested, so that they may be both QgsDataCollectionItem and QgsLayerItem
// We have to use QgsDataCollectionItem and support layer methods if necessary
class QgsWCSLayerItem : public QgsLayerItem
{
Q_OBJECT
public:
QgsWCSLayerItem( QgsDataItem* parent, QString name, QString path,
QgsWcsCapabilitiesProperty capabilitiesProperty, QgsDataSourceURI dataSourceUri, QgsWcsCoverageSummary coverageSummary );
~QgsWCSLayerItem();

QString createUri();

QgsWcsCapabilitiesProperty mCapabilities;
QgsDataSourceURI mDataSourceUri;
QgsWcsCoverageSummary mCoverageSummary;
};

class QgsWCSRootItem : public QgsDataCollectionItem
{
Q_OBJECT
public:
QgsWCSRootItem( QgsDataItem* parent, QString name, QString path );
~QgsWCSRootItem();

QVector<QgsDataItem*> createChildren();

virtual QList<QAction*> actions();

virtual QWidget * paramWidget();

public slots:
void connectionsChanged();

void newConnection();
};

#endif // QGSGDALDATAITEMS_H
211 changes: 8 additions & 203 deletions src/providers/gdal/qgsgdalprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
* *
***************************************************************************/


#include "qgslogger.h"
#include "qgsgdalproviderbase.h"
#include "qgsgdalprovider.h"
#include "qgsconfig.h"

Expand All @@ -31,7 +31,6 @@
#include "qgsrasterbandstats.h"
#include "qgsrasterlayer.h"
#include "qgsrasterpyramid.h"
#include "qgswcscapabilities.h"

#include <QImage>
#include <QSettings>
Expand Down Expand Up @@ -92,6 +91,7 @@ int CPL_STDCALL progressCallback( double dfComplete,

QgsGdalProvider::QgsGdalProvider( QString const & uri )
: QgsRasterDataProvider( uri )
, QgsGdalProviderBase()
, mValid( true )
{
QgsDebugMsg( "QgsGdalProvider: constructing with uri '" + uri + "'." );
Expand All @@ -100,7 +100,7 @@ QgsGdalProvider::QgsGdalProvider( QString const & uri )
mGdalBaseDataset = 0;
mGdalDataset = 0;

registerGdalDrivers();
QgsGdalProviderBase::registerGdalDrivers();

// To get buildSupportedRasterFileFilter the provider is called with empty uri
if ( uri.isEmpty() )
Expand All @@ -119,70 +119,7 @@ QgsGdalProvider::QgsGdalProvider( QString const & uri )
QgsDebugMsg( QString( "Trying %1 syntax, uri= %2" ).arg( vsiPrefix ).arg( dataSourceUri() ) );
}

// The uri is either a file name or encoded parameters for WCS
QString gdalUri = dataSourceUri();
if ( uri.contains( "url=" ) && uri.contains( "identifier=" ) && !QFile::exists( uri ) )
{
// - GDAL currently (4/2012) supports WCS 1.0.0 (default) and 1.1.0
// We cannot use 1.1.0 because of wrong longlat bbox send by GDAL
// and impossibility to set GridOffsets.

// - WCS 1.0.0 does not work with GDAL r24316 2012-04-25 + Mapserver 6.0.2
// 1) with geographic CRS
// GDAL sends BOUNDINGBOX=min_long,min_lat,max_lon,max_lat,urn:ogc:def:crs:EPSG::4326
// Mapserver works with min_lat,min_long,max_lon,max_lat
// OGC 07-067r5 (WCS 1.1.2) referes to OGC 06-121r3 which says:
// "The number of axes included, and the order of these axes, shall be as
// specified by the referenced CRS."
// EPSG defines for EPSG:4326 Axes: latitude, longitude
// (don't confuse with OGC:CRS84 with lon,lat order)
// Created a ticket: http://trac.osgeo.org/gdal/ticket/4639

// 2) Mapserver ignores RangeSubset (not implemented in mapserver)
// and GDAL fails with "Returned tile does not match expected band count"
// because it requested single band but recieved all bands
// Created ticket: https://github.com/mapserver/mapserver/issues/4299

// Other problems:
// - GDAL WCS fails to open 1.1 with space in RangeSubset, there is a ticket about
// it http://trac.osgeo.org/gdal/ticket/1833 without conclusion, Frank suggests
// that ServiceURL should be expected to be escaped while CoverageName should not

QgsDataSourceURI dsUri;
dsUri.setEncodedUri( uri );
gdalUri = "<WCS_GDAL>";
gdalUri += "<Version>1.0.0</Version>";
//gdalUri += "<Version>1.1.0</Version>";
// prepareUri adds ? or & if necessary, GDAL fails otherwise
gdalUri += "<ServiceURL>" + Qt::escape( QgsWcsCapabilities::prepareUri( dsUri.param( "url" ) ) ) + "</ServiceURL>";
gdalUri += "<CoverageName>" + dsUri.param( "identifier" ) + "</CoverageName>";

if ( dsUri.hasParam( "format" ) )
{
gdalUri += "<PreferredFormat>" + dsUri.param( "format" ) + "</PreferredFormat>";
}

// - CRS : there is undocumented GDAL CRS tag, but it only overrides CRS param
// in requests but the BBOX is left unchanged and thus results in server error (usually).
// 1.0 : RESPONSE_CRS
if ( dsUri.hasParam( "crs" ) )
{
gdalUri += "<GetCoverageExtra>&amp;RESPONSE_CRS=" + dsUri.param( "crs" ) + "</GetCoverageExtra>";
}
// 1.1 : Required parameters are: GridBaseCRS and GridOffsets (resolution)
// We dont have the GridOffsets here and it should be probably dynamic
// according to requested data (zoom).
// Mapserver 6.0.2 works without the GridOffsets, but other servers may not.
//QString crsUrn = "urn:ogc:def:crs:" + dsUri.param("crs").replace(":","::");
//gdalUri += "<GetCoverageExtra>&amp;GridBaseCRS=" + crsUrn + "</GetCoverageExtra>";

if ( dsUri.hasParam( "username" ) && dsUri.hasParam( "password" ) )
{
gdalUri += "<UserPwd>" + dsUri.param( "username" ) + ":" + dsUri.param( "password" ) + "</UserPwd>";
}
gdalUri += "</WCS_GDAL>";
QgsDebugMsg( "WCS uri: " + gdalUri );
}

CPLErrorReset();
mGdalBaseDataset = GDALOpen( TO8F( gdalUri ), GA_ReadOnly );
Expand Down Expand Up @@ -826,88 +763,7 @@ double QgsGdalProvider::maximumValue( int theBandNo ) const
QList<QgsColorRampShader::ColorRampItem> QgsGdalProvider::colorTable( int theBandNumber )const
{
QgsDebugMsg( "entered." );
QList<QgsColorRampShader::ColorRampItem> ct;


//Invalid band number, segfault prevention
if ( 0 >= theBandNumber )
{
QgsDebugMsg( "Invalid parameter" );
return ct;
}

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 );
ct.append( myColorRampItem );
}
else if ( myColorInterpretation == GCI_PaletteIndex )
{
QgsColorRampShader::ColorRampItem myColorRampItem;
myColorRampItem.value = ( double )myIterator;
myColorRampItem.label = QString::number( myColorRampItem.value );
//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 );
}
ct.append( myColorRampItem );
}
else
{
QgsDebugMsg( "Color interpretation type not supported yet" );
return ct;
}
}
}
}
else
{
QgsDebugMsg( "No color table found for band " + QString::number( theBandNumber ) );
return ct;
}

QgsDebugMsg( "Color table loaded successfully" );
return ct;
return QgsGdalProviderBase::colorTable( mGdalDataset, theBandNumber );
}

QgsCoordinateReferenceSystem QgsGdalProvider::crs()
Expand Down Expand Up @@ -1129,14 +985,14 @@ QgsRasterInterface::DataType QgsGdalProvider::srcDataType( int bandNo ) const
{
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, bandNo );
GDALDataType myGdalDataType = GDALGetRasterDataType( myGdalBand );
return dataTypeFormGdal( myGdalDataType );
return dataTypeFromGdal( myGdalDataType );
}

QgsRasterInterface::DataType QgsGdalProvider::dataType( int bandNo ) const
{
if ( mGdalDataType.size() == 0 ) return QgsRasterDataProvider::UnknownDataType;

return dataTypeFormGdal( mGdalDataType[bandNo-1] );
return dataTypeFromGdal( mGdalDataType[bandNo-1] );
}

int QgsGdalProvider::bandCount() const
Expand All @@ -1150,27 +1006,9 @@ int QgsGdalProvider::bandCount() const
int QgsGdalProvider::colorInterpretation( int theBandNo ) const
{
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, theBandNo );
return GDALGetRasterColorInterpretation( myGdalBand );
return colorInterpretationFromGdal( GDALGetRasterColorInterpretation( myGdalBand ) );
}

void QgsGdalProvider::registerGdalDrivers()
{
//GDALDestroyDriverManager();
GDALAllRegister();
QSettings mySettings;
QString myJoinedList = mySettings.value( "gdal/skipList", "" ).toString();
if ( !myJoinedList.isEmpty() )
{
QStringList myList = myJoinedList.split( " " );
for ( int i = 0; i < myList.size(); ++i )
{
QgsApplication::skipGdalDriver( myList.at( i ) );
}
QgsApplication::applyGdalSkippedDrivers();
}
}


bool QgsGdalProvider::isValid()
{
QgsDebugMsg( QString( "valid = %1" ).arg( mValid ) );
Expand Down Expand Up @@ -1753,8 +1591,6 @@ QGISEXTERN bool isProvider()
void buildSupportedRasterFileFilterAndExtensions( QString & theFileFiltersString, QStringList & theExtensions, QStringList & theWildcards )
{
QgsDebugMsg( "Entered" );
// first get the GDAL driver manager
QgsGdalProvider::registerGdalDrivers();

// then iterate through all of the supported drivers, adding the
// corresponding file filter
Expand Down Expand Up @@ -1963,7 +1799,7 @@ QGISEXTERN bool isValidRasterFileName( QString const & theFileNameQString, QStri
{
GDALDatasetH myDataset;

QgsGdalProvider::registerGdalDrivers();
QgsGdalProviderBase::registerGdalDrivers();

CPLErrorReset();

Expand Down Expand Up @@ -2458,34 +2294,3 @@ QGISEXTERN void buildSupportedRasterFileFilter( QString & theFileFiltersString )
buildSupportedRasterFileFilterAndExtensions( theFileFiltersString, exts, wildcards );
}

QMap<QString, QString> QgsGdalProvider::supportedMimes()
{
QMap<QString, QString> mimes;
GDALAllRegister();

QgsDebugMsg( QString( "GDAL drivers cont %1" ).arg( GDALGetDriverCount() ) );
for ( int i = 0; i < GDALGetDriverCount(); ++i )
{
GDALDriverH driver = GDALGetDriver( i );
Q_CHECK_PTR( driver );

if ( !driver )
{
QgsLogger::warning( "unable to get driver " + QString::number( i ) );
continue;
}

QString desc = GDALGetDescription( driver );

QString mimeType = GDALGetMetadataItem( driver, "DMD_MIMETYPE", "" );

if ( mimeType.isEmpty() ) continue;

desc = desc.isEmpty() ? mimeType : desc;

QgsDebugMsg( "add GDAL format " + mimeType + " " + desc );

mimes[mimeType] = desc;
}
return mimes;
}
21 changes: 2 additions & 19 deletions src/providers/gdal/qgsgdalprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,13 @@
* *
***************************************************************************/


#ifndef QGSGDALPROVIDER_H
#define QGSGDALPROVIDER_H


#include "qgscoordinatereferencesystem.h"
#include "qgsdataitem.h"
#include "qgsrasterdataprovider.h"
#include "qgsgdalproviderbase.h"
#include "qgsrectangle.h"
#include "qgscolorrampshader.h"
#include "qgsrasterbandstats.h"
Expand All @@ -36,18 +35,6 @@

class QgsRasterPyramid;

#define CPL_SUPRESS_CPLUSPLUS
#include <gdal.h>

#if defined(GDAL_VERSION_NUM) && GDAL_VERSION_NUM >= 1800
#define TO8F(x) (x).toUtf8().constData()
#define FROM8(x) QString::fromUtf8(x)
#else
#define TO8F(x) QFile::encodeName( x ).constData()
#define FROM8(x) QString::fromLocal8Bit(x)
#endif


/** \ingroup core
* A call back function for showing progress of gdal operations.
*/
Expand All @@ -66,7 +53,7 @@ class QgsCoordinateTransform;
to provide access to spatial data residing in a GDAL layers.
*/
class QgsGdalProvider : public QgsRasterDataProvider
class QgsGdalProvider : public QgsRasterDataProvider, QgsGdalProviderBase
{
Q_OBJECT

Expand Down Expand Up @@ -214,7 +201,6 @@ class QgsGdalProvider : public QgsRasterDataProvider

QList<QgsColorRampShader::ColorRampItem> colorTable( int bandNo )const;


/**
* Get metadata in a format suitable for feeding directly
* into a subset of the GUI raster properties "Metadata" tab.
Expand All @@ -231,9 +217,6 @@ class QgsGdalProvider : public QgsRasterDataProvider
void setImageCrs( QString const &crs )
{ Q_UNUSED( crs ); }

/** \brief ensures that GDAL drivers are registered, but only once */
static void registerGdalDrivers();

/** \brief Returns the sublayers of this layer - Useful for providers that manage their own layers, such as WMS */
QStringList subLayers() const;
static QStringList subLayers( GDALDatasetH dataset );
Expand Down
249 changes: 249 additions & 0 deletions src/providers/gdal/qgsgdalproviderbase.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
/***************************************************************************
qgsgdalproviderbase.cpp - Common base class for GDAL and WCS provider
-------------------
begin : November, 2010
copyright : (C) 2010 by Radim Blazek
email : radim dot blazek at gmail dot com
***************************************************************************/

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgsapplication.h"
#include "qgslogger.h"
#include "qgsgdalproviderbase.h"

#include <QSettings>

QgsGdalProviderBase::QgsGdalProviderBase()
{
QgsDebugMsg( "Entered" );

// first get the GDAL driver manager
QgsGdalProviderBase::registerGdalDrivers();
}

QgsGdalProviderBase::~QgsGdalProviderBase()
{
}

/**
* @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
*/
QList<QgsColorRampShader::ColorRampItem> QgsGdalProviderBase::colorTable( GDALDatasetH gdalDataset, int theBandNumber )const
{
QgsDebugMsg( "entered." );
QList<QgsColorRampShader::ColorRampItem> ct;

//Invalid band number, segfault prevention
if ( 0 >= theBandNumber )
{
QgsDebugMsg( "Invalid parameter" );
return ct;
}

GDALRasterBandH myGdalBand = GDALGetRasterBand( gdalDataset, 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 );
ct.append( myColorRampItem );
}
else if ( myColorInterpretation == GCI_PaletteIndex )
{
QgsColorRampShader::ColorRampItem myColorRampItem;
myColorRampItem.value = ( double )myIterator;
myColorRampItem.label = QString::number( myColorRampItem.value );
//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 );
}
ct.append( myColorRampItem );
}
else
{
QgsDebugMsg( "Color interpretation type not supported yet" );
return ct;
}
}
}
}
else
{
QgsDebugMsg( "No color table found for band " + QString::number( theBandNumber ) );
return ct;
}

QgsDebugMsg( "Color table loaded successfully" );
return ct;
}

QgsRasterInterface::DataType QgsGdalProviderBase::dataTypeFromGdal( int theGdalDataType ) const
{
switch ( theGdalDataType )
{
case GDT_Unknown:
return QgsRasterDataProvider::UnknownDataType;
break;
case GDT_Byte:
return QgsRasterDataProvider::Byte;
break;
case GDT_UInt16:
return QgsRasterDataProvider::UInt16;
break;
case GDT_Int16:
return QgsRasterDataProvider::Int16;
break;
case GDT_UInt32:
return QgsRasterDataProvider::UInt32;
break;
case GDT_Int32:
return QgsRasterDataProvider::Int32;
break;
case GDT_Float32:
return QgsRasterDataProvider::Float32;
break;
case GDT_Float64:
return QgsRasterDataProvider::Float64;
break;
case GDT_CInt16:
return QgsRasterDataProvider::CInt16;
break;
case GDT_CInt32:
return QgsRasterDataProvider::CInt32;
break;
case GDT_CFloat32:
return QgsRasterDataProvider::CFloat32;
break;
case GDT_CFloat64:
return QgsRasterDataProvider::CFloat64;
break;
case GDT_TypeCount:
// make gcc happy
break;
}
return QgsRasterDataProvider::UnknownDataType;
}

int QgsGdalProviderBase::colorInterpretationFromGdal( int gdalColorInterpretation ) const
{
switch ( gdalColorInterpretation )
{
case GCI_Undefined:
return QgsRasterDataProvider::UndefinedColorInterpretation;
break;
case GCI_GrayIndex:
return QgsRasterDataProvider::GrayIndex;
break;
case GCI_PaletteIndex:
return QgsRasterDataProvider::PaletteIndex;
break;
case GCI_RedBand:
return QgsRasterDataProvider::RedBand;
break;
case GCI_GreenBand:
return QgsRasterDataProvider::GreenBand;
break;
case GCI_BlueBand:
return QgsRasterDataProvider::BlueBand;
break;
case GCI_AlphaBand:
return QgsRasterDataProvider::AlphaBand;
break;
case GCI_HueBand:
return QgsRasterDataProvider::HueBand;
break;
case GCI_SaturationBand:
return QgsRasterDataProvider::SaturationBand;
break;
case GCI_LightnessBand:
return QgsRasterDataProvider::LightnessBand;
break;
case GCI_CyanBand:
return QgsRasterDataProvider::CyanBand;
break;
case GCI_MagentaBand:
return QgsRasterDataProvider::MagentaBand;
break;
case GCI_YellowBand:
return QgsRasterDataProvider::YellowBand;
break;
case GCI_BlackBand:
return QgsRasterDataProvider::BlackBand;
break;
case GCI_YCbCr_YBand:
return QgsRasterDataProvider::YCbCr_YBand;
break;
case GCI_YCbCr_CbBand:
return QgsRasterDataProvider::YCbCr_CbBand;
break;
case GCI_YCbCr_CrBand:
return QgsRasterDataProvider::YCbCr_CrBand;
break;
default:
break;
}
return QgsRasterDataProvider::UndefinedColorInterpretation;
}

void QgsGdalProviderBase::registerGdalDrivers()
{
GDALAllRegister();
QSettings mySettings;
QString myJoinedList = mySettings.value( "gdal/skipList", "" ).toString();
if ( !myJoinedList.isEmpty() )
{
QStringList myList = myJoinedList.split( " " );
for ( int i = 0; i < myList.size(); ++i )
{
QgsApplication::skipGdalDriver( myList.at( i ) );
}
QgsApplication::applyGdalSkippedDrivers();
}
}
59 changes: 59 additions & 0 deletions src/providers/gdal/qgsgdalproviderbase.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/***************************************************************************
qgsgdalproviderbase.h - Common base class for GDAL and WCS provider
-------------------
begin : November, 2010
copyright : (C) 2010 by Radim Blazek
email : radim dot blazek at gmail dot 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. *
* *
***************************************************************************/

#ifndef QGSGDALPROVIDERBASE_H
#define QGSGDALPROVIDERBASE_H

#include "qgsrasterdataprovider.h"
#include "qgscolorrampshader.h"

#include <QList>

#define CPL_SUPRESS_CPLUSPLUS
#include <gdal.h>

#if defined(GDAL_VERSION_NUM) && GDAL_VERSION_NUM >= 1800
#define TO8F(x) (x).toUtf8().constData()
#define FROM8(x) QString::fromUtf8(x)
#else
#define TO8F(x) QFile::encodeName( x ).constData()
#define FROM8(x) QString::fromLocal8Bit(x)
#endif

/**
\brief Base clasee for GDAL and WCS providers.
*/
class QgsGdalProviderBase
{
public:
QgsGdalProviderBase( );

~QgsGdalProviderBase();

/** \brief ensures that GDAL drivers are registered, but only once */
static void registerGdalDrivers();
protected:

QgsRasterInterface::DataType dataTypeFromGdal( int theGdalDataType ) const;

int colorInterpretationFromGdal( int gdalColorInterpretation ) const;

QList<QgsColorRampShader::ColorRampItem> colorTable( GDALDatasetH gdalDataset, int bandNo )const;
};

#endif

34 changes: 34 additions & 0 deletions src/providers/wcs/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

SET (WCS_SRCS
../gdal/qgsgdalproviderbase.cpp
qgswcsprovider.cpp
qgswcscapabilities.cpp
qgswcssourceselect.cpp
qgswcsdataitems.cpp
)
SET (WCS_MOC_HDRS
../gdal/qgsgdalproviderbase.h
qgswcsprovider.h
qgswcscapabilities.h
qgswcssourceselect.h
qgswcsdataitems.h
)

QT4_WRAP_CPP (WCS_MOC_SRCS ${WCS_MOC_HDRS})

INCLUDE_DIRECTORIES( . ../../core ../../core/raster ../../gui
../gdal
${CMAKE_CURRENT_BINARY_DIR}/../../ui
${GDAL_INCLUDE_DIR}
)

ADD_LIBRARY(wcsprovider MODULE ${WCS_SRCS} ${WCS_MOC_SRCS})

TARGET_LINK_LIBRARIES(wcsprovider
qgis_core
qgis_gui
)

INSTALL (TARGETS wcsprovider
RUNTIME DESTINATION ${QGIS_PLUGIN_DIR}
LIBRARY DESTINATION ${QGIS_PLUGIN_DIR})
43 changes: 43 additions & 0 deletions src/providers/wcs/URI
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
WCS URI
-------

Example: url=http://127.0.0.1/wcs&identifier=coverage1

WCS URI is composed of key=value pairs separated by '&'. It is the same format like query string in URL, encoded the same way. QgsDataSourceURI should be used to construct the URI to ensure that special characters are encoded properly.

Parameters:

* url (required) : WCS Server URL. Do not use VERSION in URL, because each version of WCS is using different parameter name for GetCapabilities version, see param version.

* identifier (required) : Coverage name

* time (optional) : time position or time period (beginPosition/endPosition[/timeResolution])

* format (optional) : Supported format name. Default is the first supported format with tif in name or the first supported format.

* crs (optional) : CRS in form AUTHORITY:ID, e.g. EPSG:4326. Default is EPSG:4326 if supported or the first supported CRS.

* username (optional) : Username for basic authentication.

* password (optional) : Password for basic authentication.

* IgnoreGetMapUrl (optional,hack) : If specified (set to 1), ignore GetCoverage URL advertised by GetCapabilities. May be necessary if a server is not configured properly.

* InvertAxisOrientation (optional,hack) : If specified (set to 1), switch axis in GetCoverage request. May be necessary for geographic CRS if a server is using wrong axis order.

* IgnoreAxisOrientation (optional,hack) : If specified (set to 1), do not invert axis orientation according to WCS standard for geographic CRS.


Python console example:

from PyQt4.QtCore import QString

uri = QgsDataSourceURI()
uri.setParam ("url", "http://wcs.qgis.org/1.9.0/wcs" )
uri.setParam ( "identifier", "band1_int16_noct_epsg4326" )

layer = QgsRasterLayer( QString(uri.encodedUri()), "WCS test", "wcs" )
layer.isValid()

QgsMapLayerRegistry.instance().addMapLayer(layer)

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
#ifndef QGSWCSCAPABILITIES_H
#define QGSWCSCAPABILITIES_H

//#include "qgsrasterdataprovider.h"
#include "qgsdatasourceuri.h"
#include "qgsrectangle.h"

Expand All @@ -31,60 +30,10 @@
#include <QVector>
#include <QUrl>

class QgsCoordinateTransform;
class QNetworkAccessManager;
class QNetworkReply;
class QNetworkRequest;

/*
* The following structs reflect the WCS XML schema,
* as illustrated in ... the Web Coverage Service standard, version x.x xxxx-xx-xx.
*/

/** Get Property structure */
struct QgsWcsGet
{
QString xlinkHref;
};

/** HTTP Property structure */
struct QgsWcsHTTP
{
QgsWcsGet get;
};

/** DCP Type Property structure */
struct QgsWcsDCP
{
QgsWcsHTTP http;
};

/** Version parameter */
struct QgsWcsVersion
{
QStringList allowedValues;
};

/** Operation type structure */
struct QgsWcsOperation
{
QgsWcsVersion version;
QgsWcsDCP dcp;
};

/** OperationsMetadata */
struct QgsWcsOperationsMetadata
{
QgsWcsOperation getCoverage;
};

/** ServiceerviceIdentification structure */
struct QgsWcsServiceIdentification
{
QString title;
QString abstract;
};

/** CoverageSummary structure */
struct QgsWcsCoverageSummary
{
Expand All @@ -94,36 +43,37 @@ struct QgsWcsCoverageSummary
QString abstract;
QStringList supportedCrs;
QStringList supportedFormat;
QgsRectangle wgs84BoundingBox;
QList<double> nullValues;
QgsRectangle wgs84BoundingBox; // almost useless, we need the native
QString nativeCrs;
// Map of bounding boxes, key is CRS name (srsName), e.g. EPSG:4326
QMap<QString, QgsRectangle> boundingBoxes;
QgsRectangle nativeBoundingBox;
// timePosition or timePeriod (beginPosition/endPosition[/timeResolution] - used in KVP request)
QStringList times;
QVector<QgsWcsCoverageSummary> coverageSummary;
bool described; // 1.0
// non reflecting Capabilities structure:
bool valid;
bool described;
// native size
int width;
int height;
bool hasSize;
};

/** Contents structure */
/*
struct QgsWcsContents
{
QStringList supportedCrs;
QStringList supportedFormat;
QVector<QgsWcsCoverageSummary> coverageSummary;
};
*/

/** Capability Property structure */
struct QgsWcsCapabilitiesProperty
{
QString version;
QgsWcsServiceIdentification serviceIdentification;
QgsWcsOperationsMetadata operationsMetadata;
// QgsWcsContents contents;
QString version;
QString title;
QString abstract;
QString getCoverageGetUrl;
// using QgsWcsCoverageSummary for contents for simplification
QgsWcsCoverageSummary contents;
QgsWcsCoverageSummary contents;
};

/**
\brief Data provider for OGC WCS layers.
\brief WCS Capabilities.
*/
class QgsWcsCapabilities : public QObject
{
Expand Down Expand Up @@ -157,14 +107,16 @@ class QgsWcsCapabilities : public QObject
*/
bool supportedCoverages( QVector<QgsWcsCoverageSummary> &coverageSummary );


/**
* \brief Returns a map for the hierarchy of layers
*/
void coverageParents( QMap<int, int> &parents, QMap<int, QStringList> &parentNames ) const;

//! Get coverage summare for identifier
QgsWcsCoverageSummary * coverageSummary( QString const & theIdentifier, QgsWcsCoverageSummary* parent = 0 );
//! Get coverage summary for identifier
QgsWcsCoverageSummary coverage( QString const & theIdentifier );

//! Get list of all coverage summaries
QList<QgsWcsCoverageSummary> coverages();

/**
* \brief Prepare the URI so that we can later simply append param=value
Expand All @@ -174,7 +126,6 @@ class QgsWcsCapabilities : public QObject
static QString prepareUri( QString uri );

/**Returns the GetCoverage url
* @added in 1.5
*/
QString getCoverageUrl() const;

Expand All @@ -187,11 +138,14 @@ class QgsWcsCapabilities : public QObject
bool describeCoverage( QString const &identifier, bool forceRefresh = false );

bool convertToDom( QByteArray const &xml );
bool parseDescribeCoverageDom( QByteArray const &xml, QgsWcsCoverageSummary *coverage );
bool parseDescribeCoverageDom10( QByteArray const &xml, QgsWcsCoverageSummary *coverage );
bool parseDescribeCoverageDom11( QByteArray const &xml, QgsWcsCoverageSummary *coverage );

//! set authorization header
void setAuthorization( QNetworkRequest &request ) const;

QString version() const { return mCapabilities.version; }

/**
* \brief Returns the caption error text for the last error in this provider
*
Expand All @@ -217,6 +171,29 @@ class QgsWcsCapabilities : public QObject
*/
QString lastErrorFormat();

//! Get tag name without namespace
static QString stripNS( const QString &name );

//! Get text of first child of specified name, NS is ignored
static QString firstChildText( const QDomElement &element, const QString &name );

//! Get first child of specified name, NS is ignored
static QDomElement firstChild( const QDomElement &element, const QString &name );

/** Find sub elements by path which is string of dot separated tag names.
* NS is ignored. Example path: domainSet.spatialDomain.RectifiedGrid */
static QList<QDomElement> domElements( const QDomElement &element, const QString &path );

/** Find first sub element by path which is string of dot separated tag names.
* NS is ignored. Example path: domainSet.spatialDomain.RectifiedGrid */
static QDomElement domElement( const QDomElement &element, const QString &path );

/** Get text of element specified by path */
static QString domElementText( const QDomElement &element, const QString &path );

/** Get sub elements texts by path */
static QStringList domElementsTexts( const QDomElement &element, const QString &path );

signals:

/** \brief emit a signal to notify of a progress event */
Expand All @@ -230,41 +207,42 @@ class QgsWcsCapabilities : public QObject
void capabilitiesReplyProgress( qint64, qint64 );

private:
void showMessageBox( const QString &title, const QString &text );
//! Get coverage summary for identifier
QgsWcsCoverageSummary * coverageSummary( QString const & theIdentifier, QgsWcsCoverageSummary* parent = 0 );

//! Get tag name without namespace
QString stripNS( const QString &name );
// ! Get list of all sub coverages
QList<QgsWcsCoverageSummary> coverageSummaries( QgsWcsCoverageSummary* parent = 0 );

//! Get text of first child of specified name, NS is ignored
QString firstChildText( const QDomElement &element, const QString &name );
void initCoverageSummary( QgsWcsCoverageSummary &coverageSummary );

//! Get first child of specified name, NS is ignored
QDomElement firstChild( const QDomElement &element, const QString &name );
void clear();

void showMessageBox( const QString &title, const QString &text );

QList<int> parseInts( const QString &text );
QList<double> parseDoubles( const QString &text );
QString crsUrnToAuthId( const QString &text );
/**
* \brief Retrieve and parse the (cached) Capabilities document from the server
*
* \param forceRefresh if true, ignores any previous response cached in memory
* and always contact the server for a new copy.
* \param preferredVersion - optional version KVP
*
* \retval false if the capabilities document could not be retrieved or parsed -
* see lastError() for more info
*
* When this returns, "layers" will make sense.
*
* TODO: Make network-timeout tolerant
*/
bool retrieveServerCapabilities( bool forceRefresh = false );
bool retrieveServerCapabilities( QString preferredVersion );

/** Retrieve the best WCS version supported by server and QGIS */
bool retrieveServerCapabilities( );

//! \return false if the capabilities document could not be parsed - see lastError() for more info
bool parseCapabilitiesDom( QByteArray const &xml, QgsWcsCapabilitiesProperty &capabilities );

// ------------- 1.0 --------------------
//! parse the WCS Service XML element
void parseService( QDomElement const &e, QgsWcsServiceIdentification &serviceIdentification );

//! parse the WCS Capability XML element
void parseCapability( QDomElement const &e, QgsWcsOperationsMetadata &operationsMetadata );

//! parse the WCS Layer XML element
void parseContentMetadata( QDomElement const &e, QgsWcsCoverageSummary &coverageSummary );

Expand All @@ -273,21 +251,6 @@ class QgsWcsCapabilities : public QObject
QgsWcsCoverageSummary *parent = 0 );

// ------------- 1.1 --------------------
//! parse the WCS ServiceIdentificatio XML element
void parseServiceIdentification( QDomElement const &e, QgsWcsServiceIdentification &serviceIdentification );

//! parse the WCS OperationsMetadata XML element
void parseOperationsMetadata( QDomElement const &e, QgsWcsOperationsMetadata &operationsMetadata );

//! parse the WCS GetCoverage
void parseOperation( QDomElement const & e, QgsWcsOperation& operation );

//! parse the WCS HTTP XML element
void parseHttp( QDomElement const &e, QgsWcsHTTP &http );

//! parse the WCS DCPType XML element
void parseDcp( QDomElement const &e, QgsWcsDCP &dcp );

//! parse the WCS Layer XML element
void parseCoverageSummary( QDomElement const &e, QgsWcsCoverageSummary &coverageSummary,
QgsWcsCoverageSummary *parent = 0 );
Expand Down
309 changes: 309 additions & 0 deletions src/providers/wcs/qgswcsdataitems.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,309 @@
/***************************************************************************
qgswcsdataitems.cpp
---------------------
begin : 2 July, 2012
copyright : (C) 2012 by Radim Blazek
email : radim dot blazek at gmail.com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsapplication.h"
#include "qgswcsdataitems.h"
#include "qgswcsprovider.h"
#include "qgslogger.h"
#include "qgsdatasourceuri.h"
#include "qgswcssourceselect.h"
#include "qgsowsconnection.h"
#include "qgsnewhttpconnection.h"

#include <QFileInfo>
#include <QSettings>

QgsWCSConnectionItem::QgsWCSConnectionItem( QgsDataItem* parent, QString name, QString path )
: QgsDataCollectionItem( parent, name, path )
{
mIcon = QgsApplication::getThemeIcon( "mIconWcs.png" );
}

QgsWCSConnectionItem::~QgsWCSConnectionItem()
{
QgsDebugMsg( "Entered" );
}

QVector<QgsDataItem*> QgsWCSConnectionItem::createChildren()
{
QgsDebugMsg( "Entered" );
QVector<QgsDataItem*> children;

QString encodedUri = mPath;
QgsDataSourceURI uri;
uri.setEncodedUri( encodedUri );
QgsDebugMsg( "encodedUri = " + encodedUri );

mCapabilities.setUri( uri );

// Attention: supportedLayers() gives tree leafes, not top level
if ( !mCapabilities.lastError().isEmpty() )
{
//children.append( new QgsErrorItem( this, tr( "Failed to retrieve layers" ), mPath + "/error" ) );
// TODO: show the error without adding child
return children;
}

foreach ( QgsWcsCoverageSummary coverageSummary, mCapabilities.capabilities().contents.coverageSummary )
{
// Attention, the name may be empty
QgsDebugMsg( QString::number( coverageSummary.orderId ) + " " + coverageSummary.identifier + " " + coverageSummary.title );
QString pathName = coverageSummary.identifier.isEmpty() ? QString::number( coverageSummary.orderId ) : coverageSummary.identifier;

QgsWCSLayerItem * layer = new QgsWCSLayerItem( this, coverageSummary.title, mPath + "/" + pathName, mCapabilities.capabilities(), uri, coverageSummary );

children.append( layer );
}
return children;
}

bool QgsWCSConnectionItem::equal( const QgsDataItem *other )
{
if ( type() != other->type() )
{
return false;
}
const QgsWCSConnectionItem *o = dynamic_cast<const QgsWCSConnectionItem *>( other );
if ( !o )
{
return false;
}

return ( mPath == o->mPath && mName == o->mName );
}

QList<QAction*> QgsWCSConnectionItem::actions()
{
QList<QAction*> lst;

QAction* actionEdit = new QAction( tr( "Edit..." ), this );
connect( actionEdit, SIGNAL( triggered() ), this, SLOT( editConnection() ) );
lst.append( actionEdit );

QAction* actionDelete = new QAction( tr( "Delete" ), this );
connect( actionDelete, SIGNAL( triggered() ), this, SLOT( deleteConnection() ) );
lst.append( actionDelete );

return lst;
}

void QgsWCSConnectionItem::editConnection()
{
QgsNewHttpConnection nc( 0, "/Qgis/connections-wcs/", mName );

if ( nc.exec() )
{
// the parent should be updated
mParent->refresh();
}
}

void QgsWCSConnectionItem::deleteConnection()
{
QgsOWSConnection::deleteConnection( "WCS", mName );
// the parent should be updated
mParent->refresh();
}


// ---------------------------------------------------------------------------

QgsWCSLayerItem::QgsWCSLayerItem( QgsDataItem* parent, QString name, QString path, QgsWcsCapabilitiesProperty capabilitiesProperty, QgsDataSourceURI dataSourceUri, QgsWcsCoverageSummary coverageSummary )
: QgsLayerItem( parent, name, path, QString(), QgsLayerItem::Raster, "wcs" ),
mCapabilities( capabilitiesProperty ),
mDataSourceUri( dataSourceUri ),
mCoverageSummary( coverageSummary )
{
QgsDebugMsg( "uri = " + mDataSourceUri.encodedUri() );
mUri = createUri();
// Populate everything, it costs nothing, all info about layers is collected
foreach ( QgsWcsCoverageSummary coverageSummary, mCoverageSummary.coverageSummary )
{
// Attention, the name may be empty
QgsDebugMsg( QString::number( coverageSummary.orderId ) + " " + coverageSummary.identifier + " " + coverageSummary.title );
QString pathName = coverageSummary.identifier.isEmpty() ? QString::number( coverageSummary.orderId ) : coverageSummary.identifier;
QgsWCSLayerItem * layer = new QgsWCSLayerItem( this, coverageSummary.title, mPath + "/" + pathName, mCapabilities, mDataSourceUri, coverageSummary );
mChildren.append( layer );
}

if ( mChildren.size() == 0 )
{
//mIcon = iconRaster();
mIcon = QgsApplication::getThemeIcon( "mIconWcs.png" );
}
mPopulated = true;
}

QgsWCSLayerItem::~QgsWCSLayerItem()
{
}

QString QgsWCSLayerItem::createUri()
{
if ( mCoverageSummary.identifier.isEmpty() )
return ""; // layer collection

// Number of styles must match number of layers
mDataSourceUri.setParam( "identifier", mCoverageSummary.identifier );

// TODO(?): with WCS 1.0 GetCapabilities does not contain CRS and formats,
// to get them we would need to call QgsWcsCapabilities::describeCoverage
// but it is problematic to get QgsWcsCapabilities here (copy not allowed
// by QObject, pointer is dangerous (OWS provider is changing parent))
// We leave CRS and format default for now.

QString format;
// get first supported by GDAL and server
// TODO
//QStringList mimes = QgsGdalProvider::supportedMimes().keys();
QStringList mimes;
// prefer tiff
if ( mimes.contains( "image/tiff" ) && mCoverageSummary.supportedFormat.contains( "image/tiff" ) )
{
format = "image/tiff";
}
else
{
foreach ( QString f, mimes )
{
if ( mCoverageSummary.supportedFormat.indexOf( f ) >= 0 )
{
format = f;
break;
}
}
}
if ( !format.isEmpty() )
{
mDataSourceUri.setParam( "format", format );
}

QString crs;

// TODO: prefer project CRS
// get first known if possible
QgsCoordinateReferenceSystem testCrs;
foreach ( QString c, mCoverageSummary.supportedCrs )
{
testCrs.createFromOgcWmsCrs( c );
if ( testCrs.isValid() )
{
crs = c;
break;
}
}
if ( crs.isEmpty() && mCoverageSummary.supportedCrs.size() > 0 )
{
crs = mCoverageSummary.supportedCrs.value( 0 );
}
if ( !crs.isEmpty() )
{
mDataSourceUri.setParam( "crs", crs );
}

return mDataSourceUri.encodedUri();
}

// ---------------------------------------------------------------------------

QgsWCSRootItem::QgsWCSRootItem( QgsDataItem* parent, QString name, QString path )
: QgsDataCollectionItem( parent, name, path )
{
mIcon = QgsApplication::getThemeIcon( "mIconWcs.png" );

populate();
}

QgsWCSRootItem::~QgsWCSRootItem()
{
}

QVector<QgsDataItem*>QgsWCSRootItem::createChildren()
{
QVector<QgsDataItem*> connections;
foreach ( QString connName, QgsOWSConnection::connectionList( "WCS" ) )
{
//QgsDataItem * conn = new QgsWCSConnectionItem( this, connName, mPath + "/" + connName );
QgsOWSConnection connection( "WCS", connName );
QgsDataItem * conn = new QgsWCSConnectionItem( this, connName, connection.uri().encodedUri() );

conn->setIcon( QgsApplication::getThemeIcon( "mIconConnect.png" ) );
connections.append( conn );
}
return connections;
}

QList<QAction*> QgsWCSRootItem::actions()
{
QList<QAction*> lst;

QAction* actionNew = new QAction( tr( "New Connection..." ), this );
connect( actionNew, SIGNAL( triggered() ), this, SLOT( newConnection() ) );
lst.append( actionNew );

return lst;
}


QWidget * QgsWCSRootItem::paramWidget()
{
QgsWCSSourceSelect *select = new QgsWCSSourceSelect( 0, 0, true, true );
connect( select, SIGNAL( connectionsChanged() ), this, SLOT( connectionsChanged() ) );
return select;
return 0;
}
void QgsWCSRootItem::connectionsChanged()
{
refresh();
}

void QgsWCSRootItem::newConnection()
{
QgsNewHttpConnection nc( 0, "/Qgis/connections-wcs/" );

if ( nc.exec() )
{
refresh();
}
}

// ---------------------------------------------------------------------------

static QString filterString;
static QStringList extensions = QStringList();
static QStringList wildcards = QStringList();

QGISEXTERN int dataCapabilities()
{
return QgsDataProvider::Net;
}

QGISEXTERN QgsDataItem * dataItem( QString thePath, QgsDataItem* parentItem )
{
QgsDebugMsg( "thePath = " + thePath );
if ( thePath.isEmpty() )
{
// Top level WCS
return new QgsWCSRootItem( parentItem, "WCS", "wcs:" );
}

// OWS server
QgsDebugMsg( "connection found in uri" );
return new QgsWCSConnectionItem( parentItem, "WCS", thePath );
}

QGISEXTERN QgsWCSSourceSelect * selectWidget( QWidget * parent, Qt::WFlags fl )
{
return new QgsWCSSourceSelect( parent, fl );
}
78 changes: 78 additions & 0 deletions src/providers/wcs/qgswcsdataitems.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/***************************************************************************
qgswcsdataitems.h
---------------------
begin : 2 July, 2012
copyright : (C) 2012 by Radim Blazek
email : radim dot blazek at gmail.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. *
* *
***************************************************************************/
#ifndef QGSWCSDATAITEMS_H
#define QGSWCSDATAITEMS_H

#include "qgsdataitem.h"
#include "qgsdatasourceuri.h"
#include "qgswcscapabilities.h"

class QgsWCSConnectionItem : public QgsDataCollectionItem
{
Q_OBJECT
public:
QgsWCSConnectionItem( QgsDataItem* parent, QString name, QString path );
~QgsWCSConnectionItem();

QVector<QgsDataItem*> createChildren();
virtual bool equal( const QgsDataItem *other );

virtual QList<QAction*> actions();

QgsWcsCapabilities mCapabilities;
QVector<QgsWcsCoverageSummary> mLayerProperties;

public slots:
void editConnection();
void deleteConnection();
};

// WCS Layers may be nested, so that they may be both QgsDataCollectionItem and QgsLayerItem
// We have to use QgsDataCollectionItem and support layer methods if necessary
class QgsWCSLayerItem : public QgsLayerItem
{
Q_OBJECT
public:
QgsWCSLayerItem( QgsDataItem* parent, QString name, QString path,
QgsWcsCapabilitiesProperty capabilitiesProperty, QgsDataSourceURI dataSourceUri, QgsWcsCoverageSummary coverageSummary );
~QgsWCSLayerItem();

QString createUri();

QgsWcsCapabilitiesProperty mCapabilities;
QgsDataSourceURI mDataSourceUri;
QgsWcsCoverageSummary mCoverageSummary;
};

class QgsWCSRootItem : public QgsDataCollectionItem
{
Q_OBJECT
public:
QgsWCSRootItem( QgsDataItem* parent, QString name, QString path );
~QgsWCSRootItem();

QVector<QgsDataItem*> createChildren();

virtual QList<QAction*> actions();

virtual QWidget * paramWidget();

public slots:
void connectionsChanged();

void newConnection();
};

#endif // QGSWCSDATAITEMS_H
1,573 changes: 1,573 additions & 0 deletions src/providers/wcs/qgswcsprovider.cpp

Large diffs are not rendered by default.

416 changes: 416 additions & 0 deletions src/providers/wcs/qgswcsprovider.h

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
#include "qgis.h"
#include "qgslogger.h"

#include "qgsgdalprovider.h"
#include "qgswcsprovider.h"
#include "qgswcssourceselect.h"
#include "qgswcscapabilities.h"
#include "qgsnumericsortlistviewitem.h"
Expand Down Expand Up @@ -131,18 +131,26 @@ void QgsWCSSourceSelect::addClicked( )
// Set crs only if necessary (multiple offered), so that we can decide in the
// provider if WCS 1.0 with RESPONSE_CRS has to be used. Not perfect, they can
// add more CRS in future and URI will be saved in project without any.
if ( selectedLayersCRSs().size() > 1 )
{
uri.setParam( "crs", selectedCRS() );
}
// TODO: consider again, currently if crs in url is used to set WCS coverage CRS,
// without that param user is asked for CRS
//if ( selectedLayersCRSs().size() > 1 )
//{
uri.setParam( "crs", selectedCRS() );
//}

QgsDebugMsg( "selectedFormat = " + selectedFormat() );
if ( !selectedFormat().isEmpty() )
{
uri.setParam( "format", selectedFormat() );
}

emit addRasterLayer( uri.encodedUri(), identifier, "gdal" );
QgsDebugMsg( "selectedTime = " + selectedTime() );
if ( !selectedTime().isEmpty() )
{
uri.setParam( "time", selectedTime() );
}

emit addRasterLayer( uri.encodedUri(), identifier, "wcs" );
}

void QgsWCSSourceSelect::on_mLayersTreeWidget_itemSelectionChanged()
Expand All @@ -152,7 +160,9 @@ void QgsWCSSourceSelect::on_mLayersTreeWidget_itemSelectionChanged()
QString identifier = selectedIdentifier();
if ( identifier.isEmpty() ) { return; }

mCapabilities.describeCoverage( identifier ); // 1.0 get additional info
mCapabilities.describeCoverage( identifier );

populateTimes();

populateFormats();

Expand Down Expand Up @@ -187,7 +197,7 @@ QList<QgsOWSSupportedFormat> QgsWCSSourceSelect::providerFormats()
QgsDebugMsg( "entered" );
QList<QgsOWSSupportedFormat> formats;

QMap<QString, QString> mimes = QgsGdalProvider::supportedMimes();
QMap<QString, QString> mimes = QgsWcsProvider::supportedMimes();
foreach ( QString mime, mimes.keys() )
{
QgsOWSSupportedFormat format = { mime, mimes.value( mime ) };
Expand All @@ -213,11 +223,11 @@ QStringList QgsWCSSourceSelect::selectedLayersFormats()
QString identifier = selectedIdentifier();
if ( identifier.isEmpty() ) { return QStringList(); }

QgsWcsCoverageSummary * c = mCapabilities.coverageSummary( identifier );
if ( !c ) { return QStringList(); }
QgsWcsCoverageSummary c = mCapabilities.coverage( identifier );
if ( !c.valid ) { return QStringList(); }

QgsDebugMsg( "supportedFormat = " + c->supportedFormat.join( "," ) );
return c->supportedFormat;
QgsDebugMsg( "supportedFormat = " + c.supportedFormat.join( "," ) );
return c.supportedFormat;
}

QStringList QgsWCSSourceSelect::selectedLayersCRSs()
Expand All @@ -227,10 +237,24 @@ QStringList QgsWCSSourceSelect::selectedLayersCRSs()
QString identifier = selectedIdentifier();
if ( identifier.isEmpty() ) { return QStringList(); }

QgsWcsCoverageSummary * c = mCapabilities.coverageSummary( identifier );
if ( !c ) { return QStringList(); }
QgsWcsCoverageSummary c = mCapabilities.coverage( identifier );
if ( !c.valid ) { return QStringList(); }

return c.supportedCrs;
}

QStringList QgsWCSSourceSelect::selectedLayersTimes()
{
QgsDebugMsg( "entered" );

QString identifier = selectedIdentifier();
if ( identifier.isEmpty() ) { return QStringList(); }

QgsWcsCoverageSummary c = mCapabilities.coverage( identifier );
if ( !c.valid ) { return QStringList(); }

return c->supportedCrs;
QgsDebugMsg( "times = " + c.times.join( "," ) );
return c.times;
}

void QgsWCSSourceSelect::enableLayersForCrs( QTreeWidgetItem * )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#ifndef QGSWCSSOURCESELECT_H
#define QGSWCSSOURCESELECT_H

#include "qgsowssourceselect.h"
#include "qgsdatasourceuri.h"
#include "qgisgui.h"
Expand Down Expand Up @@ -77,6 +78,7 @@ class QgsWCSSourceSelect : public QgsOWSSourceSelect
QList<QgsOWSSupportedFormat> providerFormats();
QStringList selectedLayersFormats();
QStringList selectedLayersCRSs();
QStringList selectedLayersTimes();
};
#endif // QGSWCSSOURCESELECT_H

Expand Down
258 changes: 148 additions & 110 deletions src/ui/qgsowssourceselectbase.ui
Original file line number Diff line number Diff line change
Expand Up @@ -24,29 +24,6 @@
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="3" column="0">
<widget class="QDialogButtonBox" name="mDialogButtonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Apply|QDialogButtonBox::Close|QDialogButtonBox::Help</set>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="mStatusLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Ready</string>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QTabWidget" name="mTabWidget">
<property name="enabled">
Expand All @@ -59,94 +36,100 @@
<attribute name="title">
<string>Layers</string>
</attribute>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="8">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QComboBox" name="mConnectionsComboBox"/>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="mConnectButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>C&amp;onnect</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="mNewButton">
<property name="text">
<string>&amp;New</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="mEditButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Edit</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="mDeleteButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Delete</string>
</property>
</widget>
</item>
<item row="1" column="4">
<spacer name="horizontalSpacer_1">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>8</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="5">
<widget class="QPushButton" name="mLoadButton">
<property name="toolTip">
<string>Load connections from file</string>
</property>
<property name="text">
<string>Load</string>
</property>
</widget>
</item>
<item row="1" column="6">
<widget class="QPushButton" name="mSaveButton">
<property name="toolTip">
<string>Save connections to file</string>
</property>
<property name="text">
<string>Save</string>
</property>
</widget>
</item>
<item row="1" column="7">
<widget class="QPushButton" name="mAddDefaultButton">
<property name="statusTip">
<string>Adds a few example WMS servers</string>
</property>
<property name="whatsThis">
<string comment="Adds several example WMS servers to the list"/>
</property>
<property name="text">
<string>Add default servers</string>
</property>
<item>
<widget class="QWidget" name="widget_3" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QPushButton" name="mConnectButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>C&amp;onnect</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="mNewButton">
<property name="text">
<string>&amp;New</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="mEditButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Edit</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="mDeleteButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Delete</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_1">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>82</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="mLoadButton">
<property name="toolTip">
<string>Load connections from file</string>
</property>
<property name="text">
<string>Load</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="mSaveButton">
<property name="toolTip">
<string>Save connections to file</string>
</property>
<property name="text">
<string>Save</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="mAddDefaultButton">
<property name="statusTip">
<string>Adds a few example WMS servers</string>
</property>
<property name="whatsThis">
<string comment="Adds several example WMS servers to the list"/>
</property>
<property name="text">
<string>Add default servers</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="0" colspan="8">
<item>
<widget class="QTreeWidget" name="mLayersTreeWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
Expand Down Expand Up @@ -182,7 +165,39 @@
</column>
</widget>
</item>
<item row="3" column="0" colspan="8">
<item>
<widget class="QWidget" name="widget_2" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Time</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="mTimeComboBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="mImageFormatsGroupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
Expand All @@ -201,7 +216,7 @@
</property>
</widget>
</item>
<item row="4" column="0" colspan="8">
<item>
<widget class="QGroupBox" name="mWMSGroupBox">
<property name="title">
<string>Options</string>
Expand Down Expand Up @@ -252,7 +267,7 @@
</layout>
</widget>
</item>
<item row="5" column="0" colspan="8">
<item>
<widget class="QGroupBox" name="mCRSGroupBox">
<property name="title">
<string>Coordinate Reference System</string>
Expand Down Expand Up @@ -457,6 +472,29 @@
</widget>
</widget>
</item>
<item row="3" column="0">
<widget class="QDialogButtonBox" name="mDialogButtonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Apply|QDialogButtonBox::Close|QDialogButtonBox::Help</set>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="mStatusLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Ready</string>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
Expand Down
1 change: 1 addition & 0 deletions tests/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ IF (ENABLE_TESTS)
ADD_SUBDIRECTORY(core)
ADD_SUBDIRECTORY(gui)
ADD_SUBDIRECTORY(analysis)
ADD_SUBDIRECTORY(providers)
ADD_SUBDIRECTORY(python)
ENDIF (ENABLE_TESTS)
76 changes: 76 additions & 0 deletions tests/src/providers/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Standard includes and utils to compile into all tests.

#####################################################
# Don't forget to include output directory, otherwise
# the UI file won't be wrapped!
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_SOURCE_DIR}/src/core
${CMAKE_SOURCE_DIR}/src/core/raster
${QT_INCLUDE_DIR}
${GDAL_INCLUDE_DIR}
${PROJ_INCLUDE_DIR}
${GEOS_INCLUDE_DIR}
)

#############################################################
# Compiler defines

# This define is used for tests that need to locate the test
# data under tests/testdata in the qgis source tree.
# the TEST_DATA_DIR variable is set in the top level CMakeLists.txt
ADD_DEFINITIONS(-DTEST_DATA_DIR="\\"${TEST_DATA_DIR}\\"")

ADD_DEFINITIONS(-DINSTALL_PREFIX="\\"${CMAKE_INSTALL_PREFIX}\\"")

SET(TEST_SERVER_URL "http://wcs.qgis.org/${COMPLETE_VERSION}")
ADD_DEFINITIONS(-DTEST_SERVER_URL="\\"${TEST_SERVER_URL}\\"")

#############################################################
# libraries

# because of htonl
IF (WIN32)
SET(PLATFORM_LIBRARIES wsock32)
ENDIF (WIN32)

# Since the tests are not actually installed, but rather
# run directly from the build/src/tests dir we need to
# ensure the qgis libs can be found.
IF (APPLE)
# For Mac OS X, the executable must be at the root of the bundle's executable folder
SET (CMAKE_INSTALL_NAME_DIR @executable_path/../../../src/core)
ENDIF (APPLE)

#note for tests we should not include the moc of our
#qtests in the executable file list as the moc is
#directly included in the sources
#and should not be compiled twice. Trying to include
#them in will cause an error at build time

#No relinking and full RPATH for the install tree
#See: http://www.cmake.org/Wiki/CMake_RPATH_handling#No_relinking_and_full_RPATH_for_the_install_tree

MACRO (ADD_QGIS_TEST testname testsrc)
SET(qgis_${testname}_SRCS ${testsrc} ${util_SRCS})
SET(qgis_${testname}_MOC_CPPS ${testsrc})
QT4_WRAP_CPP(qgis_${testname}_MOC_SRCS ${qgis_${testname}_MOC_CPPS})
ADD_CUSTOM_TARGET(qgis_${testname}moc ALL DEPENDS ${qgis_${testname}_MOC_SRCS})
ADD_EXECUTABLE(qgis_${testname} ${qgis_${testname}_SRCS})
ADD_DEPENDENCIES(qgis_${testname} qgis_${testname}moc)
TARGET_LINK_LIBRARIES(qgis_${testname}
${QT_QTXML_LIBRARY}
${QT_QTCORE_LIBRARY}
${QT_QTSVG_LIBRARY}
${QT_QTTEST_LIBRARY}
${PROJ_LIBRARY}
${GEOS_LIBRARY}
${GDAL_LIBRARY}
qgis_core)
ADD_TEST(qgis_${testname} ${CMAKE_CURRENT_BINARY_DIR}/../../../output/bin/qgis_${testname})
ENDMACRO (ADD_QGIS_TEST)

#############################################################
# Tests:

ADD_QGIS_TEST(wcsprovidertest testqgswcsprovider.cpp)
339 changes: 339 additions & 0 deletions tests/src/providers/testqgswcsprovider.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,339 @@
/***************************************************************************
testqgswcsprovider.cpp
--------------------------------------
Date : July 2012
Copyright : (C) 2012 by Radim Blazek
Email : radim dot blazek at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include <cmath>

#include <QtTest>
#include <QObject>
#include <QString>
#include <QStringList>
#include <QObject>
#include <QApplication>

#include <qgsdatasourceuri.h>
#include <qgsrasterlayer.h>
#include <qgsrasterdataprovider.h>
#include <qgsproviderregistry.h>
#include <qgsapplication.h>

#define TINY_VALUE std::numeric_limits<double>::epsilon() * 20

/** \ingroup UnitTests
* This is a unit test for the QgsRasterLayer class.
*/
class TestQgsWcsProvider: public QObject
{
Q_OBJECT;
private slots:
void initTestCase();// will be called before the first testfunction is executed.
void cleanupTestCase();// will be called after the last testfunction was executed.
void init() {};// will be called before each testfunction is executed.
void cleanup() {};// will be called after every testfunction.

void read();
private:
bool read( QString theIdentifier, QString theWcsUri, QString theFilePath, QString & theReport );
// Log error in html
void error( QString theMessage, QString &theReport );
// compare values and add table row in html report, set ok to false if not equal
QString compareHead();
bool compare( double wcsVal, double gdalVal, double theTolerance );
void compare( QString theParamName, int wcsVal, int gdalVal, QString &theReport, bool &theOk );
void compare( QString theParamName, double wcsVal, double gdalVal, QString &theReport, bool &theOk, double theTolerance = 0 );
void compareRow( QString theParamName, QString wcsVal, QString gdalVal, QString &theReport, bool theOk, QString theDifference = "", QString theTolerance = "" );
double tolerance( double val, int places = 6 );
QString mTestDataDir;
QString mReport;
QString mUrl;
};

//runs before all tests
void TestQgsWcsProvider::initTestCase()
{
// init QGIS's paths - true means that all path will be inited from prefix
QgsApplication::init( QString() );
QgsApplication::initQgis();
QString mySettings = QgsApplication::showSettings();
mySettings = mySettings.replace( "\n", "<br />" );
mReport += "<h1>WCS provider tests</h1>\n";
mReport += "<p>" + mySettings + "</p>";

mReport += "<style>";
mReport += ".tab { border-spacing: 0px; border-width: 1px 1px 0 0; border-style: solid; }";
mReport += ".cell { border-width: 0 0 1px 1px; border-style: solid; font-size: smaller; text-align: center}";
mReport += ".ok { background: #00ff00; }";
mReport += ".err { background: #ff0000; }";
mReport += ".errmsg { color: #ff0000; }";
mReport += "</style>";


//create some objects that will be used in all tests...
//create a raster layer that will be used in all tests...
mTestDataDir = QString( TEST_DATA_DIR ) + QDir::separator() + "raster";
qDebug() << "mTestDataDir = " << mTestDataDir;

mUrl = QString( TEST_SERVER_URL ) + QDir::separator() + "wcs";
}

//runs after all tests
void TestQgsWcsProvider::cleanupTestCase()
{
QString myReportFile = QDir::tempPath() + QDir::separator() + "qgiswcstest.html";
QFile myFile( myReportFile );
//if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) )
if ( myFile.open( QIODevice::WriteOnly ) )
{
QTextStream myQTextStream( &myFile );
myQTextStream << mReport;
myFile.close();
}
}

void TestQgsWcsProvider::read( )
{
bool ok = true;
QStringList versions;

versions << "1.0" << "1.1";

QStringList identifiers;

// identifiers in mapfile have the same name as files without .tif extension
identifiers << "band1_byte_noct_epsg4326";
identifiers << "band1_int16_noct_epsg4326";
identifiers << "band1_float32_noct_epsg4326";
identifiers << "band3_byte_noct_epsg4326";
identifiers << "band3_int16_noct_epsg4326";
identifiers << "band3_float32_noct_epsg4326";

// How to reasonably log multiple fails within this loop?
foreach ( QString version, versions )
{
foreach ( QString identifier, identifiers )
{
QString filePath = mTestDataDir + QDir::separator() + identifier + ".tif";

QgsDataSourceURI uri;
uri.setParam( "url", mUrl );
uri.setParam( "identifier", identifier );
uri.setParam( "crs", "epsg:4326" );
uri.setParam( "version", version );

if ( !read( identifier, uri.encodedUri(), filePath, mReport ) )
{
ok = false;
}
}
}
QVERIFY2( ok, "Reading data failed. See report for details." );
}

bool TestQgsWcsProvider::read( QString theIdentifier, QString theWcsUri, QString theFilePath, QString & theReport )
{
bool ok = true;

theReport += QString( "<h2>Identifier (coverage): %1</h2>" ).arg( theIdentifier );

QgsRasterDataProvider* wcsProvider = QgsRasterLayer::loadProvider( "wcs", theWcsUri );
if ( !wcsProvider || !wcsProvider->isValid() )
{
error( QString( "Cannot load WCS provider with URI: %1" ).arg( QString( theWcsUri ) ), theReport );
ok = false;
}

QgsRasterDataProvider* gdalProvider = QgsRasterLayer::loadProvider( "gdal", theFilePath );
if ( !gdalProvider || !gdalProvider->isValid() )
{
error( QString( "Cannot load GDAL provider with URI: %1" ).arg( theFilePath ), theReport );
ok = false;
}

if ( !ok ) return false;

theReport += QString( "WCS URI: %1<br>" ).arg( theWcsUri.replace( "&", "&amp;" ) );
theReport += QString( "GDAL URI: %1<br>" ).arg( theFilePath );

theReport += "<br>";
theReport += "<table class='tab'>";
theReport += compareHead();

compare( "Band count", wcsProvider->bandCount(), gdalProvider->bandCount(), theReport, ok );

compare( "Width", wcsProvider->xSize(), gdalProvider->xSize(), theReport, ok );
compare( "Height", wcsProvider->ySize(), gdalProvider->ySize(), theReport, ok );

compareRow( "Extent", wcsProvider->extent().toString(), gdalProvider->extent().toString(), theReport, wcsProvider->extent() == gdalProvider->extent() );
if ( wcsProvider->extent() != gdalProvider->extent() ) ok = false;

if ( !ok ) return false;

compare( "No data (NULL) value", wcsProvider->noDataValue(), gdalProvider->noDataValue(), theReport, ok );

theReport += "</table>";


bool allOk = true;
for ( int band = 1; band <= gdalProvider->bandCount(); band++ )
{
bool bandOk = true;
theReport += QString( "<h3>Band %1</h3>" ).arg( band );
theReport += "<table class='tab'>";
theReport += compareHead();

// Data types may differ (?)
bool typesOk = true;
compare( "Source data type", wcsProvider->srcDataType( band ), gdalProvider->srcDataType( band ), theReport, typesOk );
compare( "Data type", wcsProvider->dataType( band ), gdalProvider->dataType( band ), theReport, typesOk ) ;

bool statsOk = true;
QgsRasterBandStats wcsStats = wcsProvider->bandStatistics( band );
QgsRasterBandStats gdalStats = gdalProvider->bandStatistics( band );

// Min/max may 'slightly' differ, for big numbers however, the difference may
// be quite big, for example for Float32 with max -3.332e+38, the difference is 1.47338e+24
double tol = tolerance( gdalStats.minimumValue );
compare( "Minimum value", wcsStats.minimumValue, gdalStats.minimumValue, theReport, statsOk, tol );
tol = tolerance( gdalStats.maximumValue );
compare( "Maximum value", wcsStats.maximumValue, gdalStats.maximumValue, theReport, statsOk, tol );

// TODO: enable once fixed (WCS excludes nulls but GDAL does not)
//compare( "Cells count", wcsStats.elementCount, gdalStats.elementCount, theReport, statsOk );

tol = tolerance( gdalStats.mean );
compare( "Mean", wcsStats.mean, gdalStats.mean, theReport, statsOk, tol );

// stdDev usually differ significantly
tol = tolerance( gdalStats.stdDev, 1 );
compare( "Standard deviation", wcsStats.stdDev, gdalStats.stdDev, theReport, statsOk, tol );

theReport += "</table>";
theReport += "<br>";

if ( !bandOk )
{
allOk = false;
continue;
}

if ( !statsOk || !typesOk )
{
allOk = false;
// create values table anyway so that values are available
}

theReport += "<table><tr>";
theReport += "<td>Data comparison</td>";
theReport += "<td class='cell ok' style='border: 1px solid'>correct&nbsp;value</td>";
theReport += "<td></td>";
theReport += "<td class='cell err' style='border: 1px solid'>wrong&nbsp;value<br>expected value</td></tr>";
theReport += "</tr></table>";
theReport += "<br>";

int width = gdalProvider->xSize();
int height = gdalProvider->ySize();
int blockSize = width * height * gdalProvider->typeSize( gdalProvider->dataType( band ) ) ;
void * gdalData = malloc( blockSize );
void * wcsData = malloc( blockSize );

gdalProvider->readBlock( band, gdalProvider->extent(), width, height, gdalData );
wcsProvider->readBlock( band, gdalProvider->extent(), width, height, wcsData );

// compare data values
QString htmlTable = "<table class='tab'>";
for ( int row = 0; row < height; row ++ )
{
htmlTable += "<tr>";
for ( int col = 0; col < width; col ++ )
{
bool cellOk = true;
double wcsVal = wcsProvider->readValue( wcsData, wcsProvider->dataType( band ), row * width + col );
double gdalVal = gdalProvider->readValue( gdalData, gdalProvider->dataType( band ), row * width + col );

QString valStr;
if ( compare( wcsVal, gdalVal, 0 ) )
{
valStr = QString( "%1" ).arg( wcsVal );
}
else
{
cellOk = false;
allOk = false;
valStr = QString( "%1<br>%2" ).arg( wcsVal ).arg( gdalVal );
}
htmlTable += QString( "<td class='cell %1'>%2</td>" ).arg( cellOk ? "ok" : "err" ).arg( valStr );
}
htmlTable += "</tr>";
}
htmlTable += "</table>";

theReport += htmlTable;

free( gdalData );
free( wcsData );
}
delete wcsProvider;
delete gdalProvider;
return allOk;
}

void TestQgsWcsProvider::error( QString theMessage, QString &theReport )
{
theReport += "<font class='errmsg'>Error: ";
theReport += theMessage;
theReport += "</font>";
}

double TestQgsWcsProvider::tolerance( double val, int places )
{
// float precission is about 7 decimal digits, double about 16
// default places = 6
return 1. * pow( 10, round( log10( qAbs( val ) ) - places ) );
}

QString TestQgsWcsProvider::compareHead()
{
return "<tr><th class='cell'>Param name</th><th class='cell'>WCS (tested) value</th><th class='cell'>GDAL (expected) value</th><th class='cell'>Difference</th><th class='cell'>Tolerance</th></tr>";
}

void TestQgsWcsProvider::compare( QString theParamName, int wcsVal, int gdalVal, QString &theReport, bool &theOk )
{
bool ok = wcsVal == gdalVal;
compareRow( theParamName, QString::number( wcsVal ), QString::number( gdalVal ), theReport, ok, QString::number( wcsVal - gdalVal ) );
if ( !ok ) theOk = false;
}

bool TestQgsWcsProvider::compare( double wcsVal, double gdalVal, double theTolerance )
{
// values may be nan
return ( std::isnan( wcsVal ) && std::isnan( gdalVal ) ) || ( qAbs( wcsVal - gdalVal ) <= theTolerance );
}

void TestQgsWcsProvider::compare( QString theParamName, double wcsVal, double gdalVal, QString &theReport, bool &theOk, double theTolerance )
{
bool ok = compare( wcsVal, gdalVal, theTolerance );
compareRow( theParamName, QString::number( wcsVal ), QString::number( gdalVal ), theReport, ok, QString::number( wcsVal - gdalVal ), QString::number( theTolerance ) );
if ( !ok ) theOk = false;
}

void TestQgsWcsProvider::compareRow( QString theParamName, QString wcsVal, QString gdalVal, QString &theReport, bool theOk, QString theDifference, QString theTolerance )
{
theReport += "<tr>";
theReport += QString( "<td class='cell'>%1</td><td class='cell %2'>%3</td><td class='cell'>%4</td>" ).arg( theParamName ).arg( theOk ? "ok" : "err" ).arg( wcsVal ).arg( gdalVal );
theReport += QString( "<td class='cell'>%1</td>" ).arg( theDifference );
theReport += QString( "<td class='cell'>%1</td>" ).arg( theTolerance );
theReport += "</tr>";
}

QTEST_MAIN( TestQgsWcsProvider )
#include "moc_testqgswcsprovider.cxx"
23 changes: 23 additions & 0 deletions tests/testdata/raster/README.WCS
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
WCS test server installation
----------------------------
WCS test server is based on UMN MapServer running on qgis.org.

1. Install UMN MapServer (mapserver.org) version 6.0 or higher with WCS support (--with-wcs configure option if compiled from source)

2. Copy test data from qgis source tests/testdata/raster somewhere on server, for example to /var/www/data/test/1.9.0/tests/testdata/raster.

3. Edit WCS mapfile, for example /var/www/data/test/1.9.0/tests/testdata/raster/wcs.map and set SHAPEPATH to the path where data were copied, e.g.:
SHAPEPATH "/var/www/data/test/1.9.0/tests/testdata/raster/"

4. Create script in cgi-bin dir where mapfile is specified, e.g. /usr/lib/cgi-bin/wcstest-1.9.0:

#! /bin/sh
MS_MAPFILE=/var/www/data/test/1.9.0/tests/testdata/raster/wcs.map
export MS_MAPFILE
./mapserv

5. Configure Web server, for example if Apache is used, add rewrite rule to config file using /test/<version>/wcs path:

RewriteRule /test/1.9.0/wcs /cgi-bin/wcstest-1.9.0 [PT]

6. WARNING: If possible, don't change WCS server URL for released versions. If the server URL has to be changed for development and future versions, change also the variable TEST_SERVER_URL in tests/src/providers/CMakeLists.txt.
Binary file added tests/testdata/raster/band1_byte_ct_epsg4326.tif
Binary file not shown.
13 changes: 13 additions & 0 deletions tests/testdata/raster/band1_byte_ct_epsg4326.tif.aux.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<PAMDataset>
<PAMRasterBand band="1">
<Metadata>
<MDI key="COLOR_TABLE_RULE_RGB_0">2.000000e+00 1.280000e+02 0 255 0 255 255 0</MDI>
<MDI key="COLOR_TABLE_RULE_RGB_1">1.280000e+02 2.540000e+02 255 255 0 255 0 0</MDI>
<MDI key="COLOR_TABLE_RULES_COUNT">2</MDI>
<MDI key="STATISTICS_MAXIMUM">254</MDI>
<MDI key="STATISTICS_MEAN">135.53086419753</MDI>
<MDI key="STATISTICS_MINIMUM">2</MDI>
<MDI key="STATISTICS_STDDEV">73.957459647589</MDI>
</Metadata>
</PAMRasterBand>
</PAMDataset>
Binary file not shown.
13 changes: 13 additions & 0 deletions tests/testdata/raster/band1_byte_noct_epsg4326.tif.aux.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<PAMDataset>
<PAMRasterBand band="1">
<Metadata>
<MDI key="COLOR_TABLE_RULE_RGB_0">2.000000e+00 1.280000e+02 0 255 0 255 255 0</MDI>
<MDI key="COLOR_TABLE_RULE_RGB_1">1.280000e+02 2.540000e+02 255 255 0 255 0 0</MDI>
<MDI key="COLOR_TABLE_RULES_COUNT">2</MDI>
<MDI key="STATISTICS_MAXIMUM">254</MDI>
<MDI key="STATISTICS_MEAN">135.53086419753</MDI>
<MDI key="STATISTICS_MINIMUM">2</MDI>
<MDI key="STATISTICS_STDDEV">73.957459647589</MDI>
</Metadata>
</PAMRasterBand>
</PAMDataset>
Binary file not shown.
12 changes: 12 additions & 0 deletions tests/testdata/raster/band1_float32_noct_epsg4326.tif.aux.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<PAMDataset>
<PAMRasterBand band="1">
<Metadata>
<MDI key="COLOR_TABLE_RULE_RGB_0">0.000000e+00 0.000000e+00 255 127 0 255 127 0</MDI>
<MDI key="COLOR_TABLE_RULES_COUNT">1</MDI>
<MDI key="STATISTICS_MAXIMUM">3.3999999521444e+38</MDI>
<MDI key="STATISTICS_MEAN">2.4177777984595e+37</MDI>
<MDI key="STATISTICS_MINIMUM">-3.3319999287626e+38</MDI>
<MDI key="STATISTICS_STDDEV">1.9800745699579e+38</MDI>
</Metadata>
</PAMRasterBand>
</PAMDataset>
Binary file not shown.
13 changes: 13 additions & 0 deletions tests/testdata/raster/band1_int16_noct_epsg4326.tif.aux.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<PAMDataset>
<PAMRasterBand band="1">
<Metadata>
<MDI key="COLOR_TABLE_RULE_RGB_0">-3.211166e+04 3.276700e+02 0 255 0 255 255 0</MDI>
<MDI key="COLOR_TABLE_RULE_RGB_1">3.276700e+02 3.276700e+04 255 255 0 255 0 0</MDI>
<MDI key="COLOR_TABLE_RULES_COUNT">2</MDI>
<MDI key="STATISTICS_MAXIMUM">32767</MDI>
<MDI key="STATISTICS_MEAN">2330.024691358</MDI>
<MDI key="STATISTICS_MINIMUM">-32111</MDI>
<MDI key="STATISTICS_STDDEV">19082.231766347</MDI>
</Metadata>
</PAMRasterBand>
</PAMDataset>
Binary file not shown.
35 changes: 35 additions & 0 deletions tests/testdata/raster/band3_byte_noct_epsg4326.tif.aux.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<PAMDataset>
<PAMRasterBand band="1">
<Metadata>
<MDI key="COLOR_TABLE_RULE_RGB_0">2.000000e+00 1.280000e+02 0 255 0 255 255 0</MDI>
<MDI key="COLOR_TABLE_RULE_RGB_1">1.280000e+02 2.540000e+02 255 255 0 255 0 0</MDI>
<MDI key="COLOR_TABLE_RULES_COUNT">2</MDI>
<MDI key="STATISTICS_MAXIMUM">254</MDI>
<MDI key="STATISTICS_MEAN">135.53086419753</MDI>
<MDI key="STATISTICS_MINIMUM">2</MDI>
<MDI key="STATISTICS_STDDEV">73.957459647589</MDI>
</Metadata>
</PAMRasterBand>
<PAMRasterBand band="2">
<Metadata>
<MDI key="COLOR_TABLE_RULE_RGB_0">2.000000e+00 1.280000e+02 0 255 0 255 255 0</MDI>
<MDI key="COLOR_TABLE_RULE_RGB_1">1.280000e+02 2.540000e+02 255 255 0 255 0 0</MDI>
<MDI key="COLOR_TABLE_RULES_COUNT">2</MDI>
<MDI key="STATISTICS_MAXIMUM">254</MDI>
<MDI key="STATISTICS_MEAN">135.53086419753</MDI>
<MDI key="STATISTICS_MINIMUM">2</MDI>
<MDI key="STATISTICS_STDDEV">73.957459647589</MDI>
</Metadata>
</PAMRasterBand>
<PAMRasterBand band="3">
<Metadata>
<MDI key="COLOR_TABLE_RULE_RGB_0">2.000000e+00 1.280000e+02 0 255 0 255 255 0</MDI>
<MDI key="COLOR_TABLE_RULE_RGB_1">1.280000e+02 2.540000e+02 255 255 0 255 0 0</MDI>
<MDI key="COLOR_TABLE_RULES_COUNT">2</MDI>
<MDI key="STATISTICS_MAXIMUM">254</MDI>
<MDI key="STATISTICS_MEAN">135.53086419753</MDI>
<MDI key="STATISTICS_MINIMUM">2</MDI>
<MDI key="STATISTICS_STDDEV">73.957459647589</MDI>
</Metadata>
</PAMRasterBand>
</PAMDataset>
Binary file not shown.
32 changes: 32 additions & 0 deletions tests/testdata/raster/band3_float32_noct_epsg4326.tif.aux.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<PAMDataset>
<PAMRasterBand band="1">
<Metadata>
<MDI key="COLOR_TABLE_RULE_RGB_0">0.000000e+00 0.000000e+00 255 127 0 255 127 0</MDI>
<MDI key="COLOR_TABLE_RULES_COUNT">1</MDI>
<MDI key="STATISTICS_MAXIMUM">3.3999999521444e+38</MDI>
<MDI key="STATISTICS_MEAN">2.4177777984595e+37</MDI>
<MDI key="STATISTICS_MINIMUM">-3.3319999287626e+38</MDI>
<MDI key="STATISTICS_STDDEV">1.9800745699579e+38</MDI>
</Metadata>
</PAMRasterBand>
<PAMRasterBand band="2">
<Metadata>
<MDI key="COLOR_TABLE_RULE_RGB_0">0.000000e+00 0.000000e+00 255 127 0 255 127 0</MDI>
<MDI key="COLOR_TABLE_RULES_COUNT">1</MDI>
<MDI key="STATISTICS_MAXIMUM">3.3999999521444e+38</MDI>
<MDI key="STATISTICS_MEAN">2.4177777984595e+37</MDI>
<MDI key="STATISTICS_MINIMUM">-3.3319999287626e+38</MDI>
<MDI key="STATISTICS_STDDEV">1.9800745699579e+38</MDI>
</Metadata>
</PAMRasterBand>
<PAMRasterBand band="3">
<Metadata>
<MDI key="COLOR_TABLE_RULE_RGB_0">0.000000e+00 0.000000e+00 255 127 0 255 127 0</MDI>
<MDI key="COLOR_TABLE_RULES_COUNT">1</MDI>
<MDI key="STATISTICS_MAXIMUM">3.3999999521444e+38</MDI>
<MDI key="STATISTICS_MEAN">2.4177777984595e+37</MDI>
<MDI key="STATISTICS_MINIMUM">-3.3319999287626e+38</MDI>
<MDI key="STATISTICS_STDDEV">1.9800745699579e+38</MDI>
</Metadata>
</PAMRasterBand>
</PAMDataset>
Binary file not shown.
35 changes: 35 additions & 0 deletions tests/testdata/raster/band3_int16_noct_epsg4326.tif.aux.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<PAMDataset>
<PAMRasterBand band="1">
<Metadata>
<MDI key="COLOR_TABLE_RULE_RGB_0">-3.211166e+04 3.276700e+02 0 255 0 255 255 0</MDI>
<MDI key="COLOR_TABLE_RULE_RGB_1">3.276700e+02 3.276700e+04 255 255 0 255 0 0</MDI>
<MDI key="COLOR_TABLE_RULES_COUNT">2</MDI>
<MDI key="STATISTICS_MAXIMUM">32767</MDI>
<MDI key="STATISTICS_MEAN">2330.024691358</MDI>
<MDI key="STATISTICS_MINIMUM">-32111</MDI>
<MDI key="STATISTICS_STDDEV">19082.231766347</MDI>
</Metadata>
</PAMRasterBand>
<PAMRasterBand band="2">
<Metadata>
<MDI key="COLOR_TABLE_RULE_RGB_0">-3.211166e+04 3.276700e+02 0 255 0 255 255 0</MDI>
<MDI key="COLOR_TABLE_RULE_RGB_1">3.276700e+02 3.276700e+04 255 255 0 255 0 0</MDI>
<MDI key="COLOR_TABLE_RULES_COUNT">2</MDI>
<MDI key="STATISTICS_MAXIMUM">32767</MDI>
<MDI key="STATISTICS_MEAN">2330.024691358</MDI>
<MDI key="STATISTICS_MINIMUM">-32111</MDI>
<MDI key="STATISTICS_STDDEV">19082.231766347</MDI>
</Metadata>
</PAMRasterBand>
<PAMRasterBand band="3">
<Metadata>
<MDI key="COLOR_TABLE_RULE_RGB_0">-3.211166e+04 3.276700e+02 0 255 0 255 255 0</MDI>
<MDI key="COLOR_TABLE_RULE_RGB_1">3.276700e+02 3.276700e+04 255 255 0 255 0 0</MDI>
<MDI key="COLOR_TABLE_RULES_COUNT">2</MDI>
<MDI key="STATISTICS_MAXIMUM">32767</MDI>
<MDI key="STATISTICS_MEAN">2330.024691358</MDI>
<MDI key="STATISTICS_MINIMUM">-32111</MDI>
<MDI key="STATISTICS_STDDEV">19082.231766347</MDI>
</Metadata>
</PAMRasterBand>
</PAMDataset>
Loading