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;
}

int 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:

int dataTypeFromGdal( int theGdalDataType ) const;

int colorInterpretationFromGdal( int gdalColorInterpretation ) const;

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

#endif

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

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
Expand All @@ -15,6 +17,7 @@ SET (WCS_MOC_HDRS
QT4_WRAP_CPP (WCS_MOC_SRCS ${WCS_MOC_HDRS})

INCLUDE_DIRECTORIES( . ../../core ../../core/raster ../../gui
../gdal
${CMAKE_CURRENT_BINARY_DIR}/../../ui
${GDAL_INCLUDE_DIR}
)
Expand Down
138 changes: 120 additions & 18 deletions src/providers/wcs/qgswcscapabilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -458,13 +458,13 @@ QDomElement QgsWcsCapabilities::firstChild( const QDomElement &element, const QS
QString tagName = stripNS( el.tagName() );
if ( tagName == name )
{
QgsDebugMsg( name + " found" );
//QgsDebugMsg( name + " found" );
return el;
}
}
n1 = n1.nextSibling();
}
QgsDebugMsg( name + " not found" );
//QgsDebugMsg( name + " not found" );
return QDomElement();
}

Expand All @@ -473,7 +473,7 @@ QString QgsWcsCapabilities::firstChildText( const QDomElement &element, const QS
QDomElement el = firstChild( element, name );
if ( !el.isNull() )
{
QgsDebugMsg( name + " : " + el.text() );
//QgsDebugMsg( name + " : " + el.text() );
return el.text();
}
return QString();
Expand All @@ -497,7 +497,7 @@ QList<QDomElement> QgsWcsCapabilities::domElements( const QDomElement &element,
QString tagName = stripNS( el.tagName() );
if ( tagName == name )
{
QgsDebugMsg( name + " found" );
//QgsDebugMsg( name + " found" );
if ( names.size() == 0 )
{
list.append( el );
Expand Down Expand Up @@ -551,6 +551,38 @@ QList<int> QgsWcsCapabilities::parseInts( const QString &text )
return list;
}

QList<double> QgsWcsCapabilities::parseDoubles( const QString &text )
{
QList<double> list;
foreach( QString s, text.split( " " ) )
{
int i;
bool ok;
list.append( s.toDouble( &ok ) );
if ( !ok )
{
list.clear();
return list;
}
}
return list;
}

QString QgsWcsCapabilities::crsUrnToAuthId ( const QString &text )
{
QString urnCrs = text;

QString authid;

// example: urn:ogc:def:crs:EPSG::4326
QStringList split = urnCrs.remove("urn:ogc:def:crs:").split(":");
if ( split.size() == 3 )
{
authid = QString("%1:%2").arg( split.value(0) ).arg(split.value(2) );
}
return authid;
}

// ------------------------ 1.0 ----------------------------------------------
void QgsWcsCapabilities::parseService( const QDomElement &e, QgsWcsServiceIdentification &serviceIdentification ) // 1.0
{
Expand Down Expand Up @@ -727,10 +759,15 @@ bool QgsWcsCapabilities::parseDescribeCoverageDom10( QByteArray const &xml, QgsW
{
QString tagName = stripNS( el.tagName() );

if ( tagName == "requestResponseCRSs" )
if ( tagName == "requestResponseCRSs" ) // requestCRSs + responseCRSs alternative
{
coverage->supportedCrs << el.text();
}
else if ( tagName == "nativeCRSs" ) // optional
{
coverage->nativeCrs = el.text();
}
// TODO: requestCRSs, responseCRSs - must be then implemented also in provider
}
n1 = n1.nextSibling();
}
Expand Down Expand Up @@ -758,11 +795,12 @@ bool QgsWcsCapabilities::parseDescribeCoverageDom10( QByteArray const &xml, QgsW

// spatialDomain and Grid/RectifiedGrid are optional according to specificationi.
// If missing, we cannot get native resolution and size.
QDomElement gridElement = domElement( coverageOfferingElement, "domainSet.spatialDomain.Grid" );
QDomElement gridElement = domElement( coverageOfferingElement, "domainSet.spatialDomain.RectifiedGrid" );

if ( gridElement.isNull() )
{
gridElement = domElement( coverageOfferingElement, "domainSet.spatialDomain.RectifiedGrid" );
// Grid has also GridEnvelope from which we can get coverage size but it does not
gridElement = domElement( coverageOfferingElement, "domainSet.spatialDomain.Grid" );
}

if ( !gridElement.isNull() )
Expand All @@ -771,16 +809,60 @@ bool QgsWcsCapabilities::parseDescribeCoverageDom10( QByteArray const &xml, QgsW
QList<int> high = parseInts( domElementText( gridElement, "limits.GridEnvelope.high" ) );
if ( low.size() == 2 && high.size() == 2 )
{
coverage->width = high[0] - low[0] + 1;
coverage->height = high[1] - low[1] + 1;
coverage->hasSize = true;
double width = high[0] - low[0] + 1;
double height = high[1] - low[1] + 1;
if ( width > 0 && height > 0 )
{
coverage->width = width;
coverage->height = height;
coverage->hasSize = true;
}
}
// RectifiedGrid has also gml:origin which we dont need I think (attention however
// it should contain gml:Point but mapserver 6.0.3 / WCS 1.0.0 is using gml:pos instead)
// RectifiedGrid also contains 2 gml:offsetVector which could be used to get resolution
// but it should be sufficient to calc resolution from size

// TODO: check if coverage is rotated, in that case probably treat as without size
// or recalc resolution from rotated grid to base CRS
}

QList<QDomElement> envelopeElements = domElements( coverageOfferingElement, "domainSet.spatialDomain.Envelope" );

QgsDebugMsg( QString( "%1 envelopeElements found" ).arg( envelopeElements.size() ) );

foreach( QDomElement el, envelopeElements )
{
QString srsName = el.attribute( "srsName" );

QList<QDomElement> posElements = domElements( el, "pos" );
if ( posElements.size() != 2 )
{
QgsDebugMsg ("Wrong number of pos elements");
continue;
}

QList<double> low = parseDoubles( posElements.value(0).text() );
QList<double> high = parseDoubles( posElements.value(1).text() );
if ( low.size() == 2 && high.size() == 2 )
{
QgsRectangle box ( low[0], low[1], high[0], high[1] ) ;
coverage->boundingBoxes.insert( srsName, box );
QgsDebugMsg ( "Envelope: " + srsName + " : " + box.toString() );
}
}

// Find native bounding box
if ( !coverage->nativeCrs.isEmpty() )
{
foreach ( QString srsName, coverage->boundingBoxes.keys() )
{
if ( srsName == coverage->nativeCrs )
{
coverage->nativeBoundingBox = coverage->boundingBoxes.value( srsName );
}
}
}
QgsDebugMsg( QString( "width = %1 height = %2" ).arg( coverage->width ).arg( coverage->height ) );

coverage->described = true;

Expand Down Expand Up @@ -821,20 +903,40 @@ bool QgsWcsCapabilities::parseDescribeCoverageDom11( QByteArray const &xml, QgsW

foreach( QDomElement el, boundingBoxElements )
{
if ( el.attribute( "crs" ) != "urn:ogc:def:crs:OGC::imageCRS" ) continue;
QString authid = crsUrnToAuthId ( el.attribute( "crs" ) );
QList<double> low = parseDoubles( domElementText( el, "LowerCorner" ) );
QList<double> high = parseDoubles( domElementText( el, "UpperCorner" ) );

QList<int> low = parseInts( domElementText( el, "LowerCorner" ) );
QList<int> high = parseInts( domElementText( el, "UpperCorner" ) );
if ( low.size() == 2 && high.size() == 2 )
if ( low.size() != 2 && high.size() != 2 ) continue;

if ( el.attribute( "crs" ) == "urn:ogc:def:crs:OGC::imageCRS" )
{
coverage->width = high[0] - low[0] + 1;
coverage->height = high[1] - low[1] + 1;
coverage->width = (int) ( high[0] - low[0] + 1 );
coverage->height = (int) ( high[1] - low[1] + 1 );
coverage->hasSize = true;
}
break;
else
{
QgsRectangle box ( low[0], low[1], high[0], high[1] ) ;
coverage->boundingBoxes.insert( authid, box );
QgsDebugMsg ( "BoundingBox: " + authid + " : " + box.toString() );
}
}
QgsDebugMsg( QString( "width = %1 height = %2" ).arg( coverage->width ).arg( coverage->height ) );

// Each georectified coverage should have GridCRS
QDomElement gridCRSElement = domElement( docElem, "CoverageDescription.Domain.SpatialDomain.GridCRS" );

if ( !gridCRSElement.isNull() )
{
QString crsUrn = firstChildText ( gridCRSElement, "GridBaseCRS" );
coverage->nativeCrs = crsUrnToAuthId( crsUrn );
QgsDebugMsg( "nativeCrs = " + coverage->nativeCrs );

// TODO: consider getting coverage size from GridOffsets (resolution)
// if urn:ogc:def:crs:OGC::imageCRS BoundingBox was not found
}

coverage->described = true;

return true;
Expand Down
11 changes: 9 additions & 2 deletions src/providers/wcs/qgswcscapabilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,16 @@ struct QgsWcsCoverageSummary
QString abstract;
QStringList supportedCrs;
QStringList supportedFormat;
QgsRectangle wgs84BoundingBox;
QgsRectangle wgs84BoundingBox; // almost useless, we need the native
// Map of bounding boxes, key is CRS name (srsName), e.g. EPSG:4326
QString nativeCrs;
QMap<QString, QgsRectangle> boundingBoxes;
QgsRectangle nativeBoundingBox;
QVector<QgsWcsCoverageSummary> coverageSummary;
bool described; // 1.0
// non reflecting directly Capabilities structure:
int width;
// native size
int width;
int height;
bool hasSize;
};
Expand Down Expand Up @@ -261,6 +266,8 @@ class QgsWcsCapabilities : public QObject
QString domElementText( const QDomElement &element, const QString &path );

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
*
Expand Down
551 changes: 173 additions & 378 deletions src/providers/wcs/qgswcsprovider.cpp

Large diffs are not rendered by default.

22 changes: 13 additions & 9 deletions src/providers/wcs/qgswcsprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

#include "qgswcscapabilities.h"
#include "qgsrasterdataprovider.h"
#include "qgsgdalproviderbase.h"
#include "qgsrectangle.h"

#include <QString>
Expand Down Expand Up @@ -59,7 +60,7 @@ class QNetworkRequest;
data residing in a OGC Web Map Service.
*/
class QgsWcsProvider : public QgsRasterDataProvider
class QgsWcsProvider : public QgsRasterDataProvider, QgsGdalProviderBase
{
Q_OBJECT

Expand Down Expand Up @@ -121,7 +122,7 @@ class QgsWcsProvider : public QgsRasterDataProvider
void readBlock( int theBandNo, int xBlock, int yBlock, void *block );

/** Download cache */
void getCache( int bandNo, QgsRectangle const & viewExtent, int width, int height );
void getCache( int bandNo, QgsRectangle const & viewExtent, int width, int height, QString crs = "" );

/** Return the extent for this data layer
*/
Expand Down Expand Up @@ -158,6 +159,7 @@ class QgsWcsProvider : public QgsRasterDataProvider
QString name() const;
QString description() const;
void reloadData();
QList<QgsColorRampShader::ColorRampItem> colorTable( int bandNo )const;

// WMS specific, maybe to be removed from QgsRasterDataProvider
void addLayers( QStringList const &layers, QStringList const &styles = QStringList() ) { Q_UNUSED( layers ); Q_UNUSED( styles ); }
Expand Down Expand Up @@ -224,21 +226,23 @@ class QgsWcsProvider : public QgsRasterDataProvider
*/
QString prepareUri( QString uri ) const;

//QString layerMetadata( QgsWmsLayerProperty &layer );
QString layerMetadata( );
QString coverageMetadata( QgsWcsCoverageSummary c );

//! remove query item and replace it with a new value
void setQueryItem( QUrl &url, QString key, QString value );

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

//! Convert data type from GDAL to QGIS
int dataTypeFormGdal( int theGdalDataType ) const;

//! Release cache resources
void clearCache();

//! Create html cell (used by metadata)
QString htmlCell ( const QString &text );

//! Create html row with 2 cells (used by metadata)
QString htmlRow ( const QString &text1, const QString &text2 );

//! Data source URI of the WCS for this layer
QString mHttpUri;

Expand Down Expand Up @@ -295,8 +299,8 @@ class QgsWcsProvider : public QgsRasterDataProvider
/** \brief Cell value representing no data. e.g. -9999, indexed from 0 */
QList<double> mNoDataValue;

/** Source data types */
//QVector<QgsRasterDataProvider::DataType> mDataTypes;
/** Color tables indexed from 0 */
QList< QList<QgsColorRampShader::ColorRampItem> > mColorTables;

/**
* Last Service Exception Report from the WCS
Expand Down
8 changes: 5 additions & 3 deletions src/providers/wcs/qgswcssourceselect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,12 @@ 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 )
{
// 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() )
Expand Down