71 changes: 34 additions & 37 deletions src/core/raster/qgsrasterdataprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
class QImage;
class QgsPoint;
class QByteArray;
#include <QVariant>

#define TINY_VALUE std::numeric_limits<double>::epsilon() * 20
#define RASTER_HISTOGRAM_BINS 256
Expand Down Expand Up @@ -68,7 +69,11 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast
Histogram = 1 << 5,
Size = 1 << 6, // has fixed source type
Create = 1 << 7, //create new datasets
Remove = 1 << 8 //delete datasets
Remove = 1 << 8, //delete datasets
IdentifyValue = 1 << 9,
IdentifyText = 1 << 10,
IdentifyHtml = 1 << 11,
IdentifyFeature = 1 << 12 // WMS GML -> feature
};

// This is modified copy of GDALColorInterp
Expand All @@ -95,6 +100,17 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast
/*! Max current value */ ColorInterpretationMax = 17
};

enum IdentifyFormat
{
IdentifyFormatValue = 0,
IdentifyFormatText = 1,
IdentifyFormatHtml = 1 << 1,
// In future it should be possible to get from GetFeatureInfo (WMS) in GML
// vector features. It is possible to use a user type with QVariant if
// a class is declared with Q_DECLARE_METATYPE
IdentifyFormatFeature = 1 << 2
};

// Progress types
enum RasterProgressType
{
Expand Down Expand Up @@ -430,47 +446,28 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast
*/
virtual QString metadata() = 0;

/** \brief Identify raster value(s) found on the point position
* @param point coordinates in data source CRS
* @return list of pointers to data blocks for all bands,
* caller is responsible to free the allocated memory,
* readValue() may be used to get values
* @note theBinCount, theMinimun and theMaximum not optional in python bindings
*/
// TODO: Consider QVariant or similar instead of void*
virtual QMap<int, void *> identify( const QgsPoint & point );

/**
* \brief Identify details from a server (e.g. WMS) from the last screen update
*
* \param[in] point The pixel coordinate (as it was displayed locally on screen)
*
* \return A text document containing the return from the WMS server
*
* \note WMS Servers prefer to receive coordinates in image space, therefore
* this function expects coordinates in that format.
/** \brief Identify raster value(s) found on the point position. The context
* parameters theExtent, theWidth and theHeigh are important to identify
* on the same zoom level as a displayed map and to do effective
* caching (WCS). If context params are not specified the highest
* resolution is used. capabilities() may be used to test if format
* is supported by provider. Values are set to 'no data' or empty string
* if point is outside data source extent.
*
* \note The arbitraryness of the returned document is enforced by WMS standards
* up to at least v1.3.0
* @param thePoint coordinates in data source CRS
* @param theFormat result format
* @param theExtent context extent
* @param theWidth context width
* @param theHeight context height
* @return map of values for all bands, keys are band numbers (from 1), empty
* if failed
*/
virtual QString identifyAsText( const QgsPoint& point ) = 0;
virtual QMap<int, QVariant> identify( const QgsPoint & thePoint, IdentifyFormat theFormat, const QgsRectangle &theExtent = QgsRectangle(), int theWidth = 0, int theHeight = 0 );

/**
* \brief Identify details from a server (e.g. WMS) from the last screen update
*
* \param[in] point The pixel coordinate (as it was displayed locally on screen)
*
* \return A html document containing the return from the WMS server
*
* \note WMS Servers prefer to receive coordinates in image space, therefore
* this function expects coordinates in that format.
*
* \note The arbitraryness of the returned document is enforced by WMS standards
* up to at least v1.3.0
*
* \note added in 1.5
*/
virtual QString identifyAsHtml( const QgsPoint& point ) = 0;

QMap<QString, QString> identify( const QgsPoint & thePoint, const QgsRectangle &theExtent = QgsRectangle(), int theWidth = 0, int theHeight = 0 );

/**
* \brief Returns the caption error text for the last error in this provider
Expand Down
2 changes: 2 additions & 0 deletions src/core/raster/qgsrasterlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,7 @@ bool QgsRasterLayer::hasStatistics( int theBandNo )
* @param theResults QMap to hold the pixel values at thePoint for each layer in the raster file
* @return False if WMS layer and true otherwise
*/
#if 0
bool QgsRasterLayer::identify( const QgsPoint& thePoint, QMap<QString, QString>& theResults )
{
theResults.clear();
Expand Down Expand Up @@ -1026,6 +1027,7 @@ QString QgsRasterLayer::identifyAsHtml( const QgsPoint& thePoint )

return mDataProvider->identifyAsHtml( thePoint );
}
#endif

/**
* @note Note implemented yet
Expand Down
8 changes: 4 additions & 4 deletions src/core/raster/qgsrasterlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -474,20 +474,20 @@ class CORE_EXPORT QgsRasterLayer : public QgsMapLayer
bool hasCompatibleSymbology( const QgsMapLayer& theOther ) const;

/** \brief Identify raster value(s) found on the point position */
bool identify( const QgsPoint & point, QMap<QString, QString>& results );
//bool identify( const QgsPoint & point, QMap<QString, QString>& results );

/** \brief Identify raster value(s) found on the point position
* @note available in python bindings as identifyMap
*/
bool identify( const QgsPoint & point, QMap<int, QString>& results );
//bool identify( const QgsPoint & point, QMap<int, QString>& results );

/** \brief Identify arbitrary details from the WMS server found on the point position */
QString identifyAsText( const QgsPoint & point );
//QString identifyAsText( const QgsPoint & point );

/** \brief Identify arbitrary details from the WMS server found on the point position
* @note added in 1.5
*/
QString identifyAsHtml( const QgsPoint & point );
//QString identifyAsHtml( const QgsPoint & point );

/** \brief Currently returns always false */
bool isEditable() const;
Expand Down
5 changes: 3 additions & 2 deletions src/mapserver/qgswmsserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1298,13 +1298,14 @@ int QgsWMSServer::featureInfoFromRasterLayer( QgsRasterLayer* layer,
{
Q_UNUSED( version );

if ( !infoPoint || !layer )
if ( !infoPoint || !layer || !layer->dataProvider() )
{
return 1;
}

QMap<QString, QString> attributes;
layer->identify( *infoPoint, attributes );
// TODO: use context extent, width height (comes with request) to use WCS cache
attributes = layer->dataProvider()->identify( *infoPoint );

for ( QMap<QString, QString>::const_iterator it = attributes.constBegin(); it != attributes.constEnd(); ++it )
{
Expand Down
147 changes: 66 additions & 81 deletions src/providers/gdal/qgsgdalprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
#include "qgsrasterlayer.h"
#include "qgsrasterpyramid.h"

#include "qgspoint.h"

#include <QImage>
#include <QSettings>
#include <QColor>
Expand Down Expand Up @@ -826,102 +828,97 @@ int QgsGdalProvider::yBlockSize() const
int QgsGdalProvider::xSize() const { return mWidth; }
int QgsGdalProvider::ySize() const { return mHeight; }

QMap<int, void *> QgsGdalProvider::identify( const QgsPoint & point )
QMap<int, QVariant> QgsGdalProvider::identify( const QgsPoint & thePoint, IdentifyFormat theFormat, const QgsRectangle &theExtent, int theWidth, int theHeight )
{
Q_UNUSED( theFormat );
Q_UNUSED( theExtent );
Q_UNUSED( theWidth );
Q_UNUSED( theHeight );
QgsDebugMsg( "Entered" );
QMap<int, void *> results;
if ( !mExtent.contains( point ) )

QMap<int, QVariant> results;

if ( theFormat != IdentifyFormatValue ) return results;

if ( !extent().contains( thePoint ) )
{
// Outside the raster
for ( int i = 1; i <= GDALGetRasterCount( mGdalDataset ); i++ )
for ( int bandNo = 1; bandNo <= bandCount(); bandNo++ )
{
void * data = QgsMalloc( dataTypeSize( i ) );
QgsRasterBlock::writeValue( data, dataType( i ), 0, noDataValue( i ) );
results.insert( i, data );
results.insert( bandNo, noDataValue( bandNo ) );
}
return results;
}
else
{
double x = point.x();
double y = point.y();

// Calculate the row / column where the point falls
double xres = ( mExtent.xMaximum() - mExtent.xMinimum() ) / mWidth;
double yres = ( mExtent.yMaximum() - mExtent.yMinimum() ) / mHeight;
QgsRectangle myExtent = theExtent;
if ( myExtent.isEmpty() ) myExtent = extent();

// Offset, not the cell index -> flor
int col = ( int ) floor(( x - mExtent.xMinimum() ) / xres );
int row = ( int ) floor(( mExtent.yMaximum() - y ) / yres );
if ( theWidth == 0 ) theWidth = xSize();
if ( theHeight == 0 ) theHeight = ySize();

// QgsDebugMsg( "row = " + QString::number( row ) + " col = " + QString::number( col ) );
// Calculate the row / column where the point falls
double xres = ( myExtent.width() ) / theWidth;
double yres = ( myExtent.height() ) / theHeight;

for ( int i = 1; i <= GDALGetRasterCount( mGdalDataset ); i++ )
{
GDALRasterBandH gdalBand = GDALGetRasterBand( mGdalDataset, i );
// Offset, not the cell index -> flor
int col = ( int ) floor(( thePoint.x() - myExtent.xMinimum() ) / xres );
int row = ( int ) floor(( myExtent.yMaximum() - thePoint.y() ) / yres );

// QgsDebugMsg( "row = " + QString::number( row ) + " col = " + QString::number( col ) );

int r = 0;
int c = 0;
int width = 1;
int height = 1;
int r = 0;
int c = 0;
int width = 1;
int height = 1;

// GDAL ECW driver in GDAL < 1.9.2 read whole row if single pixel (nYSize == 1)
// was requested which made identify very slow -> use 2x2 matrix
// but other drivers may be optimised for 1x1 -> conditional
// GDAL ECW driver in GDAL < 1.9.2 read whole row if single pixel (nYSize == 1)
// was requested which made identify very slow -> use 2x2 matrix
// but other drivers may be optimised for 1x1 -> conditional
#if !defined(GDAL_VERSION_NUM) || GDAL_VERSION_NUM < 1920
if ( strcmp( GDALGetDriverShortName( GDALGetDatasetDriver( mGdalDataset ) ), "ECW" ) == 0 )
{
width = 2;
height = 2;
if ( col == mWidth - 1 && mWidth > 1 )
{
col--;
c++;
}
if ( row == mHeight - 1 && mHeight > 1 )
{
row--;
r++;
}
}
if ( strcmp( GDALGetDriverShortName( GDALGetDatasetDriver( mGdalDataset ) ), "ECW" ) == 0 )
{
width = 2;
height = 2;
if ( col == mWidth - 1 && mWidth > 1 )
{
col--;
c++;
}
if ( row == mHeight - 1 && mHeight > 1 )
{
row--;
r++;
}
}
#endif
int typeSize = dataTypeSize( i );
void * tmpData = QgsMalloc( typeSize * width * height );

CPLErr err = GDALRasterIO( gdalBand, GF_Read, col, row, width, height,
tmpData, width, height,
( GDALDataType ) mGdalDataType[i-1], 0, 0 );
double xMin = myExtent.xMinimum() + col * xres;
double xMax = xMin + xres * width;
double yMax = myExtent.yMaximum() - row * yres;
double yMin = yMax - yres * height;
QgsRectangle pixelExtent( xMin, yMin, xMax, yMax );

if ( err != CPLE_None )
{
QgsLogger::warning( "RasterIO error: " + QString::fromUtf8( CPLGetLastErrorMsg() ) );
}
void * data = QgsMalloc( typeSize );
memcpy( data, ( void* )(( char* )tmpData + ( r*width + c )*typeSize ), typeSize );
results.insert( i, data );
for ( int i = 1; i <= bandCount(); i++ )
{
QgsRasterBlock * myBlock = block( i, pixelExtent, width, height );

QgsFree( tmpData );
if ( !myBlock )
{
results.clear();
return results;
}
}

return results;
}
double value = myBlock->value( r, c );

#if 0
bool QgsGdalProvider::identify( const QgsPoint& thePoint, QMap<QString, QString>& theResults )
{
QMap<int, QString> results;
identify( thePoint, results );
for ( int i = 1; i <= GDALGetRasterCount( mGdalDataset ); i++ )
{
theResults[ generateBandName( i )] = results.value( i );
results.insert( i, value );
}
return true;
return results;
}
#endif

int QgsGdalProvider::capabilities() const
{
int capability = QgsRasterDataProvider::Identify
| QgsRasterDataProvider::IdentifyValue
| QgsRasterDataProvider::ExactResolution
| QgsRasterDataProvider::EstimatedMinimumMaximum
| QgsRasterDataProvider::BuildPyramids
Expand Down Expand Up @@ -1016,18 +1013,6 @@ bool QgsGdalProvider::isValid()
return mValid;
}

QString QgsGdalProvider::identifyAsText( const QgsPoint& point )
{
Q_UNUSED( point );
return QString( "Not implemented" );
}

QString QgsGdalProvider::identifyAsHtml( const QgsPoint& point )
{
Q_UNUSED( point );
return QString( "Not implemented" );
}

QString QgsGdalProvider::lastErrorTitle()
{
return QString( "Not implemented" );
Expand Down
28 changes: 1 addition & 27 deletions src/providers/gdal/qgsgdalprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,33 +121,7 @@ class QgsGdalProvider : public QgsRasterDataProvider, QgsGdalProviderBase
*/
bool isValid();

/** \brief Identify raster value(s) found on the point position */
//bool identify( const QgsPoint & point, QMap<QString, QString>& results );

//bool identify( const QgsPoint & point, QMap<int, QString>& results );

QMap<int, void *> identify( const QgsPoint & point );

/**
* \brief Identify details from a GDAL layer from the last screen update
*
* \param point[in] The pixel coordinate (as it was displayed locally on screen)
*
* \return A text document containing the return from the GDAL layer
*
*/
QString identifyAsText( const QgsPoint& point );

/**
* \brief Identify details from a GDAL layer from the last screen update
*
* \param point[in] The pixel coordinate (as it was displayed locally on screen)
*
* \return A text document containing the return from the GDAL layer
*
* \note added in 1.5
*/
QString identifyAsHtml( const QgsPoint& point );
QMap<int, QVariant> identify( const QgsPoint & thePoint, IdentifyFormat theFormat, const QgsRectangle &theExtent = QgsRectangle(), int theWidth = 0, int theHeight = 0 );

/**
* \brief Returns the caption error text for the last error in this provider
Expand Down
5 changes: 3 additions & 2 deletions src/providers/grass/qgis.g.info.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,8 @@ int main( int argc, char **argv )

if ( col < 0 || col > window.cols || row < 0 || row > window.rows )
{
fprintf( stdout, "value:out\n" );
//fprintf( stdout, "value:out\n" );
fprintf( stdout, "value:nan\n" );
}
else
{
Expand Down Expand Up @@ -245,7 +246,7 @@ int main( int argc, char **argv )
}
if ( G_is_null_value( ptr, rast_type ) )
{
fprintf( stdout, "value:null\n" );
fprintf( stdout, "value:nan\n" );
}
else
{
Expand Down
64 changes: 28 additions & 36 deletions src/providers/grass/qgsgrassrasterprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -417,40 +417,39 @@ int QgsGrassRasterProvider::yBlockSize() const
int QgsGrassRasterProvider::xSize() const { return mCols; }
int QgsGrassRasterProvider::ySize() const { return mRows; }

QMap<int, void *> QgsGrassRasterProvider::identify( const QgsPoint & thePoint )
QMap<int, QVariant> QgsGrassRasterProvider::identify( const QgsPoint & thePoint, IdentifyFormat theFormat, const QgsRectangle &theExtent, int theWidth, int theHeight )
{
QgsDebugMsg( "Entered" );
QMap<int, void *> results;
QMap<int, QVariant> results;

if ( theFormat != IdentifyFormatValue ) return results;

if ( !extent().contains( thePoint ) )
{
results.insert( 1, noDataValue( 1 ) );
return results;
}

// TODO: use doubles instead of strings
//theResults = QgsGrass::query( mGisdbase, mLocation, mMapset, mMapName, QgsGrass::Raster, thePoint.x(), thePoint.y() );
QString strValue = mRasterValue.value( thePoint.x(), thePoint.y() );

// attention, value tool does his own tricks with grass identify() so it stops to refresh values outside extent or null values e.g.

double value = noDataValue( 1 );
bool ok;
double value = mRasterValue.value( thePoint.x(), thePoint.y(), &ok );

if ( strValue != "out" && strValue != "null" )
{
bool ok;
value = strValue.toDouble( & ok );
if ( !ok )
{
value = 999999999;
QgsDebugMsg( "Cannot convert string to double" );
}
}
void * data = malloc( dataTypeSize( 1 ) );
QgsRasterBlock::writeValue( data, dataType( 1 ), 0, value );
if ( !ok ) return results;

results.insert( 1, data );
QgsDebugMsg( "strValue = " + strValue );
if ( qIsNaN( value ) ) value = noDataValue( 1 );

results.insert( 1, value );

return results;
}

int QgsGrassRasterProvider::capabilities() const
{
int capability = QgsRasterDataProvider::Identify
| QgsRasterDataProvider::IdentifyValue
| QgsRasterDataProvider::ExactResolution
| QgsRasterDataProvider::ExactMinimumMaximum
| QgsRasterDataProvider::Size;
Expand Down Expand Up @@ -522,18 +521,6 @@ bool QgsGrassRasterProvider::isValid()
return mValid;
}

QString QgsGrassRasterProvider::identifyAsText( const QgsPoint& point )
{
Q_UNUSED( point );
return QString( "Not implemented" );
}

QString QgsGrassRasterProvider::identifyAsHtml( const QgsPoint& point )
{
Q_UNUSED( point );
return QString( "Not implemented" );
}

QString QgsGrassRasterProvider::lastErrorTitle()
{
return QString( "Not implemented" );
Expand Down Expand Up @@ -634,22 +621,27 @@ QgsGrassRasterValue::~QgsGrassRasterValue()
}
}

QString QgsGrassRasterValue::value( double x, double y )
double QgsGrassRasterValue::value( double x, double y, bool *ok )
{
QString value = "error";
if ( !mProcess )
return value; // throw some exception?
*ok = false;
double value = std::numeric_limits<double>::quiet_NaN();

if ( !mProcess ) return value;

QString coor = QString( "%1 %2\n" ).arg( x ).arg( y );
QgsDebugMsg( "coor : " + coor );
mProcess->write( coor.toAscii() ); // how to flush, necessary?
mProcess->waitForReadyRead();
QString str = mProcess->readLine().trimmed();
QgsDebugMsg( "read from stdout : " + str );

// TODO: use doubles instead of strings

QStringList list = str.trimmed().split( ":" );
if ( list.size() == 2 )
{
value = list[1];
if ( list[1] == "error" ) return value;
value = list[1].toDouble( ok );
}
return value;
}
Expand Down
29 changes: 4 additions & 25 deletions src/providers/grass/qgsgrassrasterprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@ class QgsGrassRasterValue
QgsGrassRasterValue( );
~QgsGrassRasterValue();
void start( QString gisdbase, QString location, QString mapset, QString map );
// returns raster value as string or "null" or "error"
QString value( double x, double y );
// returns raster value, NaN for no data
// ok is set to true if ok or false on error
double value( double x, double y, bool *ok );
private:
QString mGisdbase; // map gisdabase
QString mLocation; // map location name (not path!)
Expand Down Expand Up @@ -140,29 +141,7 @@ class QgsGrassRasterProvider : public QgsRasterDataProvider
*/
bool isValid();

/** \brief Identify raster value(s) found on the point position */
//bool identify( const QgsPoint & point, QMap<QString, QString>& results );
QMap<int, void *> identify( const QgsPoint & thePoint );

/**
* \brief Identify details from a GRASS layer from the last screen update
*
* \param point[in] The pixel coordinate (as it was displayed locally on screen)
*
* \return A text document containing the return from the GRASS layer
*/
QString identifyAsText( const QgsPoint& point );

/**
* \brief Identify details from a GRASS layer from the last screen update
*
* \param point[in] The pixel coordinate (as it was displayed locally on screen)
*
* \return A text document containing the return from the GRASS layer
*
* \note added in 1.5
*/
QString identifyAsHtml( const QgsPoint& point );
QMap<int, QVariant> identify( const QgsPoint & thePoint, IdentifyFormat theFormat, const QgsRectangle &theExtent = QgsRectangle(), int theWidth = 0, int theHeight = 0 );

/**
* \brief Returns the caption error text for the last error in this provider
Expand Down
103 changes: 52 additions & 51 deletions src/providers/wcs/qgswcsprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1467,6 +1467,7 @@ int QgsWcsProvider::capabilities() const
{
int capability = NoCapabilities;
capability |= QgsRasterDataProvider::Identify;
capability |= QgsRasterDataProvider::IdentifyValue;
capability |= QgsRasterDataProvider::Histogram;

if ( mHasSize )
Expand Down Expand Up @@ -1633,56 +1634,66 @@ QString QgsWcsProvider:: htmlRow( const QString &text1, const QString &text2 )
return "<tr>" + htmlCell( text1 ) + htmlCell( text2 ) + "</tr>";
}

QMap<int, void *> QgsWcsProvider::identify( const QgsPoint & thePoint )
//QMap<int, void *> QgsWcsProvider::identify( const QgsPoint & thePoint )
QMap<int, QVariant> QgsWcsProvider::identify( const QgsPoint & thePoint, IdentifyFormat theFormat, const QgsRectangle &theExtent, int theWidth, int theHeight )
{
QgsDebugMsg( "Entered" );
QMap<int, void *> results;
QMap<int, QVariant> results;

if ( theFormat != IdentifyFormatValue ) return results;

if ( !extent().contains( thePoint ) )
{
// Outside the raster
for ( int i = 1; i <= bandCount(); i++ )
{
void * data = QgsMalloc( dataTypeSize( i ) );
QgsRasterBlock::writeValue( data, dataType( i ), 0, noDataValue( i ) );
results.insert( i, data );
results.insert( i, noDataValue( i ) );
}
return results;
}

// It would be nice to use last cached block if possible, unfortunately we don't know
// at which resolution identify() is called. It may happen, that user zoomed in with
// layer switched off, canvas resolution increased, but provider cache was not refreshed.
// In that case the resolution is too low and identify() could give wron results.
// So we have to read always a block of data around the point on highest resolution
// if not already cached.
// TODO: change provider identify() prototype to pass also the resolution

int width, height;
if ( mHasSize )
QgsRectangle myExtent = theExtent;
int maxSize = 2000;
// if context size is to large we have to cut it, in that case caching big
// big part does not make sense
if ( myExtent.isEmpty() || theWidth == 0 || theHeight == 0 ||
theWidth > maxSize || theHeight > maxSize )
{
width = mWidth;
height = mHeight;
// context missing, use an area around the point and highest resolution if known

// 1000 is bad in any case, either not precise or too much data requests
if ( theWidth == 0 ) theWidth = mHasSize ? mWidth : 1000;
if ( theHeight == 0 ) theHeight = mHasSize ? mHeight : 1000;

if ( myExtent.isEmpty() ) myExtent = extent();

double xRes = myExtent.width() / theWidth;
double yRes = myExtent.height() / theHeight;

if ( !mCachedGdalDataset ||
!mCachedViewExtent.contains( thePoint ) ||
mCachedViewWidth == 0 || mCachedViewHeight == 0 ||
mCachedViewExtent.width() / mCachedViewWidth - xRes > TINY_VALUE ||
mCachedViewExtent.height() / mCachedViewHeight - yRes > TINY_VALUE )
{
int half = 50;
QgsRectangle extent( thePoint.x() - xRes * half, thePoint.y() - yRes * half,
thePoint.x() + xRes * half, thePoint.y() + yRes * half );
getCache( 1, extent, 2*half, 2*half );

}
}
else
{
// Bad in any case, either not precise or too much data requests
width = height = 1000;
}
double xRes = mCoverageExtent.width() / width;
double yRes = mCoverageExtent.height() / height;

if ( !mCachedGdalDataset ||
!mCachedViewExtent.contains( thePoint ) ||
mCachedViewWidth == 0 || mCachedViewHeight == 0 ||
mCachedViewExtent.width() / mCachedViewWidth - xRes > TINY_VALUE ||
mCachedViewExtent.height() / mCachedViewHeight - yRes > TINY_VALUE )
{
int half = 50;
QgsRectangle extent( thePoint.x() - xRes * half, thePoint.y() - yRes * half,
thePoint.x() + xRes * half, thePoint.y() + yRes * half );
getCache( 1, extent, 2*half, 2*half );

// Use context -> effective caching (usually, if context is constant)
QgsDebugMsg( "Using context extent and resolution" );
if ( !mCachedGdalDataset ||
mCachedViewExtent != theExtent ||
mCachedViewWidth != theWidth ||
mCachedViewHeight != theHeight )
{
getCache( 1, theExtent, theWidth, theHeight );
}
}

if ( !mCachedGdalDataset ||
Expand All @@ -1695,8 +1706,8 @@ QMap<int, void *> QgsWcsProvider::identify( const QgsPoint & thePoint )
double y = thePoint.y();

// Calculate the row / column where the point falls
xRes = ( mCachedViewExtent.xMaximum() - mCachedViewExtent.xMinimum() ) / mCachedViewWidth;
yRes = ( mCachedViewExtent.yMaximum() - mCachedViewExtent.yMinimum() ) / mCachedViewHeight;
double xRes = ( mCachedViewExtent.width() ) / mCachedViewWidth;
double yRes = ( mCachedViewExtent.height() ) / mCachedViewHeight;

// Offset, not the cell index -> flor
int col = ( int ) floor(( x - mCachedViewExtent.xMinimum() ) / xRes );
Expand All @@ -1708,33 +1719,23 @@ QMap<int, void *> QgsWcsProvider::identify( const QgsPoint & thePoint )
{
GDALRasterBandH gdalBand = GDALGetRasterBand( mCachedGdalDataset, i );

void * data = QgsMalloc( dataTypeSize( i ) );
double value;
CPLErr err = GDALRasterIO( gdalBand, GF_Read, col, row, 1, 1,
data, 1, 1, ( GDALDataType ) mGdalDataType[i-1], 0, 0 );
&value, 1, 1, GDT_Float64, 0, 0 );

if ( err != CPLE_None )
{
QgsLogger::warning( "RasterIO error: " + QString::fromUtf8( CPLGetLastErrorMsg() ) );
results.clear();
return results;
}

results.insert( i, data );
results.insert( i, value );
}

return results;
}

QString QgsWcsProvider::identifyAsText( const QgsPoint &point )
{
Q_UNUSED( point );
return QString( "Not implemented" );
}

QString QgsWcsProvider::identifyAsHtml( const QgsPoint &point )
{
Q_UNUSED( point );
return QString( "Not implemented" );
}

QgsCoordinateReferenceSystem QgsWcsProvider::crs()
{
return mCrs;
Expand Down
5 changes: 1 addition & 4 deletions src/providers/wcs/qgswcsprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,7 @@ class QgsWcsProvider : public QgsRasterDataProvider, QgsGdalProviderBase
int xSize() const;
int ySize() const;
QString metadata();
//bool identify( const QgsPoint& thePoint, QMap<QString, QString>& theResults );
QMap<int, void *> identify( const QgsPoint & thePoint );
QString identifyAsHtml( const QgsPoint& point );
QString identifyAsText( const QgsPoint& point );
QMap<int, QVariant> identify( const QgsPoint & thePoint, IdentifyFormat theFormat, const QgsRectangle &theExtent = QgsRectangle(), int theWidth = 0, int theHeight = 0 );
QString lastErrorTitle();
QString lastError();
QString lastErrorFormat();
Expand Down
116 changes: 62 additions & 54 deletions src/providers/wms/qgswmsprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3194,12 +3194,13 @@ int QgsWmsProvider::capabilities() const
{
// Collect all the test results into one bitmask
capability |= QgsRasterDataProvider::Identify;
break;
if ( f == "text/html" ) capability |= QgsRasterDataProvider::IdentifyHtml;
else if ( f == "text/plain" ) capability |= QgsRasterDataProvider::IdentifyText;
}
}
}

//QgsDebugMsg( "exiting with '" + QString( capability ) + "'." );
QgsDebugMsg( "exiting with '" + QString::number( capability, 2 ) + "'." );

return capability;
}
Expand Down Expand Up @@ -3756,16 +3757,56 @@ QString QgsWmsProvider::metadata()
return metadata;
}

QStringList QgsWmsProvider::identifyAs( const QgsPoint& point, QString format )
QMap<int, QVariant> QgsWmsProvider::identify( const QgsPoint & thePoint, IdentifyFormat theFormat, const QgsRectangle &theExtent, int theWidth, int theHeight )
{
QgsDebugMsg( "Entering." );
QStringList results;
QStringList resultStrings;
QMap<int, QVariant> results;

// Collect which layers to query on
QString format;
if ( theFormat == IdentifyFormatHtml )
{
if ( !( capabilities() & IdentifyHtml ) ) return results;
format = "text/html";
}
else if ( theFormat == IdentifyFormatText )
{
if ( !( capabilities() & IdentifyText ) ) return results;
format = "text/plain";
}
else
{
return results;
}

QStringList queryableLayers = QStringList();
QString text = "";
if ( !extent().contains( thePoint ) )
{
results.insert( 1, "" );
return results;
}

QgsRectangle myExtent = theExtent;
if ( myExtent.isEmpty() )
{
// use full extent
myExtent = extent();
}

// We don't know highest resolution, so it is difficult to guess any
// but that is why theExtent, theWidth, theHeight params are here
if ( theWidth == 0 ) theWidth = 1000; // just some number
if ( theHeight == 0 ) theHeight = 1000;

// Point in BBOX/WIDTH/HEIGHT coordinates
// No need to fiddle with extent origin not covered by layer extent, I believe
double xRes = myExtent.width() / theWidth;
double yRes = myExtent.height() / theHeight;

QgsPoint point;
point.setX( floor(( thePoint.x() - myExtent.xMinimum() ) / xRes ) );
point.setY( floor(( myExtent.yMaximum() - thePoint.y() ) / yRes ) );

// Collect which layers to query on
//according to the WMS spec for 1.3, the order of x - and y - coordinates is inverted for geographical CRS
bool changeXY = false;
if ( !mIgnoreAxisOrientation && ( mCapabilities.version == "1.3.0" || mCapabilities.version == "1.3" ) )
Expand All @@ -3790,10 +3831,10 @@ QStringList QgsWmsProvider::identifyAs( const QgsPoint& point, QString format )

// Compose request to WMS server
QString bbox = QString( changeXY ? "%2,%1,%4,%3" : "%1,%2,%3,%4" )
.arg( mCachedViewExtent.xMinimum(), 0, 'f', 16 )
.arg( mCachedViewExtent.yMinimum(), 0, 'f', 16 )
.arg( mCachedViewExtent.xMaximum(), 0, 'f', 16 )
.arg( mCachedViewExtent.yMaximum(), 0, 'f', 16 );
.arg( myExtent.xMinimum(), 0, 'f', 16 )
.arg( myExtent.yMinimum(), 0, 'f', 16 )
.arg( myExtent.xMaximum(), 0, 'f', 16 )
.arg( myExtent.yMaximum(), 0, 'f', 16 );

// Test for which layers are suitable for querying with
for ( QStringList::const_iterator
Expand All @@ -3818,8 +3859,8 @@ QStringList QgsWmsProvider::identifyAs( const QgsPoint& point, QString format )
setQueryItem( requestUrl, "REQUEST", "GetFeatureInfo" );
setQueryItem( requestUrl, "BBOX", bbox );
setQueryItem( requestUrl, crsKey, mImageCrs );
setQueryItem( requestUrl, "WIDTH", QString::number( mCachedViewWidth ) );
setQueryItem( requestUrl, "HEIGHT", QString::number( mCachedViewHeight ) );
setQueryItem( requestUrl, "WIDTH", QString::number( theWidth ) );
setQueryItem( requestUrl, "HEIGHT", QString::number( theHeight ) );
setQueryItem( requestUrl, "LAYERS", *layers );
setQueryItem( requestUrl, "STYLES", *styles );
setQueryItem( requestUrl, "FORMAT", mImageMimeType );
Expand Down Expand Up @@ -3853,56 +3894,23 @@ QStringList QgsWmsProvider::identifyAs( const QgsPoint& point, QString format )
QCoreApplication::processEvents( QEventLoop::ExcludeUserInputEvents );
}

results << mIdentifyResult;
resultStrings << mIdentifyResult;
}

QgsDebugMsg( "Exiting with: " + results.join( "\n------\n" ) );
return results;
}

QString QgsWmsProvider::identifyAsText( const QgsPoint &point )
{
if ( !mCapabilities.capability.request.getFeatureInfo.format.contains( "text/plain" ) )
return tr( "Layer cannot be queried in plain text." );

QStringList list = identifyAs( point, "text/plain" );
QString str;

if ( list.isEmpty() )
if ( theFormat == IdentifyFormatHtml )
{
return tr( "Layer cannot be queried." );
str = "<table>\n<tr><td>" + resultStrings.join( "</td></tr>\n<tr><td>" ) + "</td></tr>\n</table>";
}
else
else if ( theFormat == IdentifyFormatText )
{
return list.join( "\n-------------\n" );
str = resultStrings.join( "\n-------------\n" );
}
}

QString QgsWmsProvider::identifyAsHtml( const QgsPoint &point )
{
QString format;
results.insert( 1, str );

foreach ( QString f, mSupportedGetFeatureFormats )
{
if ( mCapabilities.capability.request.getFeatureInfo.format.contains( f ) )
{
format = f;
break;
}
}

Q_ASSERT( !format.isEmpty() );

QStringList results = identifyAs( point, format );

if ( format == "text/html" )
{
return "<table>\n<tr><td>" + results.join( "</td></tr>\n<tr><td>" ) + "</td></tr>\n</table>";
}
else
{
// TODO format text/xml
return "<table>\n<tr><td><pre>\n" + results.join( "\n</pre></td></tr>\n<tr><td><pre>\n" ) + "\n</pre></td></tr>\n</table>";
}
return results;
}

void QgsWmsProvider::identifyReplyFinished()
Expand Down
33 changes: 2 additions & 31 deletions src/providers/wms/qgswmsprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -653,36 +653,7 @@ class QgsWmsProvider : public QgsRasterDataProvider
*/
QString metadata();


/**
* \brief Identify details from a WMS from the last screen update
*
* \param point[in] The pixel coordinate (as it was displayed locally on screen)
*
* \return A html document containing the return from the WMS server
*
* \note WMS prefer to receive coordinates in image space, therefore
* this function expects coordinates in that format.
*
* \note The arbitraryness of the returned document is enforced by WMS standards
* up to at least v1.3.0
*/
QString identifyAsHtml( const QgsPoint& point );

/**
* \brief Identify details from a WMS from the last screen update
*
* \param point[in] The pixel coordinate (as it was displayed locally on screen)
*
* \return A text document containing the return from the WMS server
*
* \note WMS prefer to receive coordinates in image space, therefore
* this function expects coordinates in that format.
*
* \note The arbitraryness of the returned document is enforced by WMS standards
* up to at least v1.3.0
*/
QString identifyAsText( const QgsPoint& point );
QMap<int, QVariant> identify( const QgsPoint & thePoint, IdentifyFormat theFormat, const QgsRectangle &theExtent = QgsRectangle(), int theWidth = 0, int theHeight = 0 );

/**
* \brief Returns the caption error text for the last error in this provider
Expand Down Expand Up @@ -884,7 +855,7 @@ class QgsWmsProvider : public QgsRasterDataProvider
*/
QString prepareUri( QString uri ) const;

QStringList identifyAs( const QgsPoint &point, QString format );
//QStringList identifyAs( const QgsPoint &point, QString format );

QString layerMetadata( QgsWmsLayerProperty &layer );

Expand Down
21 changes: 14 additions & 7 deletions tests/src/python/test_qgsrasterlayer.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from PyQt4 import QtGui

from qgis.core import (QgsRasterLayer,
QgsRasterDataProvider,
QgsColorRampShader,
QgsContrastEnhancement,
QgsMapLayerRegistry,
Expand Down Expand Up @@ -50,21 +51,27 @@ def testIdentify(self):
assert myRasterLayer.isValid(), myMessage
myPoint = QgsPoint(786690, 3345803)
#print 'Extents: %s' % myRasterLayer.extent().toString()
myResult, myRasterValues = myRasterLayer.identify(myPoint)
assert myResult
#myResult, myRasterValues = myRasterLayer.identify(myPoint)
#assert myResult
myRasterValues = myRasterLayer.dataProvider().identify(myPoint, QgsRasterDataProvider.IdentifyFormatValue )

assert len( myRasterValues ) > 0

# Get the name of the first band
myBandName = myRasterValues.keys()[0]
myExpectedName = QString('Band 1')
myBand = myRasterValues.keys()[0]
#myExpectedName = QString('Band 1')
myExpectedBand = 1
myMessage = 'Expected "%s" got "%s" for first raster band name' % (
myExpectedName, myBandName)
assert myExpectedName == myBandName, myMessage
myExpectedBand, myBand)
assert myExpectedBand == myBand, myMessage

# Convert each band value to a list of ints then to a string

myValues = myRasterValues.values()
myIntValues = []
for myValue in myValues:
myIntValues.append(int(str(myValue)))
#myIntValues.append(int(str(myValue)))
myIntValues.append( myValue.toInt()[0] )
myValues = str(myIntValues)
myExpectedValues = '[127, 141, 112, 72, 86, 126, 156, 211, 170]'
myMessage = 'Expected: %s\nGot: %s' % (myValues, myExpectedValues)
Expand Down