Skip to content

Commit

Permalink
raster identify context, QVariant values, enables wcs caching and mak…
Browse files Browse the repository at this point in the history
…es wms more robust
  • Loading branch information
blazek committed Oct 17, 2012
1 parent 79be949 commit df85492
Show file tree
Hide file tree
Showing 19 changed files with 436 additions and 452 deletions.
50 changes: 10 additions & 40 deletions python/core/raster/qgsrasterdataprovider.sip
Expand Up @@ -54,6 +54,14 @@ class QgsRasterDataProvider : QgsDataProvider, QgsRasterInterface
/*! Max current value */ ColorInterpretationMax = 17
};

enum IdentifyFormat
{
IdentifyFormatValue,
IdentifyFormatText,
IdentifyFormatHtml,
IdentifyFormatFeature
};

// Progress types
enum RasterProgressType
{
Expand Down Expand Up @@ -304,47 +312,9 @@ class QgsRasterDataProvider : QgsDataProvider, QgsRasterInterface
*/
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.
*
* \note The arbitraryness of the returned document is enforced by WMS standards
* up to at least v1.3.0
*/
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
14 changes: 7 additions & 7 deletions python/core/raster/qgsrasterlayer.sip
Expand Up @@ -305,21 +305,21 @@ class QgsRasterLayer : 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 /Out/ );
//bool identify( const QgsPoint & point, QMap<QString, QString>& results /Out/ );

/** \brief Identify raster value(s) found on the point position */
bool identifyMap( const QgsPoint & point, QMap<int, QString>& results /Out/ );
%MethodCode
sipRes = sipCpp->identify( *a0, *a1 );
%End
// bool identifyMap( const QgsPoint & point, QMap<int, QString>& results /Out/ );
//%MethodCode
// sipRes = sipCpp->identify( *a0, *a1 );
//%End

/** \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
41 changes: 32 additions & 9 deletions src/app/qgsmaptoolidentify.cpp
Expand Up @@ -38,6 +38,7 @@
#include <QCursor>
#include <QPixmap>
#include <QStatusBar>
#include <QVariant>

QgsMapToolIdentify::QgsMapToolIdentify( QgsMapCanvas* canvas )
: QgsMapTool( canvas )
Expand Down Expand Up @@ -315,14 +316,14 @@ bool QgsMapToolIdentify::identifyRasterLayer( QgsRasterLayer *layer, int x, int
{
bool res = true;

if ( !layer )
return false;
if ( !layer ) return false;

QgsRasterDataProvider *dprovider = layer->dataProvider();
if ( dprovider && ( dprovider->capabilities() & QgsRasterDataProvider::Identify ) == 0 )
if ( !dprovider || !( dprovider->capabilities() & QgsRasterDataProvider::Identify ) )
{
return false;
}

QMap< QString, QString > attributes, derivedAttributes;
QgsPoint idPoint = mCanvas->getCoordinateTransform()->toMapCoordinates( x, y );
try
{
Expand All @@ -335,8 +336,9 @@ bool QgsMapToolIdentify::identifyRasterLayer( QgsRasterLayer *layer, int x, int
return false;
}

QString type;
if ( !layer->extent().contains( idPoint ) ) return false;

#if 0
if ( layer->providerType() == "wms" )
{
type = tr( "WMS layer" );
Expand Down Expand Up @@ -366,13 +368,34 @@ bool QgsMapToolIdentify::identifyRasterLayer( QgsRasterLayer *layer, int x, int
res = false;
}
}
else
#endif

// TODO: extent, width, heigh are approximated only if layer is reprojected!!!!
// How to do it better? We dont know source resolution used to reproject the layer.

QgsRectangle viewExtent = mCanvas->extent();
if ( mCanvas->hasCrsTransformEnabled() && dprovider->crs() != mCanvas->mapRenderer()->destinationCrs() )
{
type = tr( "Raster" );
res = layer->extent().contains( idPoint ) && layer->identify( idPoint, attributes );
viewExtent = toLayerCoordinates( layer, viewExtent );
}

if ( res )
// cut by layer extent to use possible cache, the same done when drawing
viewExtent = dprovider->extent().intersect( &viewExtent );

double mapUnitsPerPixel = mCanvas->mapUnitsPerPixel();
// Width and height are calculated from not projected extent and we hope that
// are similar to source width and height used to reproject layer for drawing.
int width = mCanvas->extent().width() / mapUnitsPerPixel;
int height = mCanvas->extent().height() / mapUnitsPerPixel;

QMap< QString, QString > attributes, derivedAttributes;

attributes = dprovider->identify( idPoint, viewExtent, width, height );

QString type;
type = tr( "Raster" );

if ( attributes.size() > 0 )
{
derivedAttributes.insert( tr( "(clicked coordinate)" ), idPoint.toString() );
results()->addFeature( layer, type, attributes, derivedAttributes );
Expand Down
13 changes: 10 additions & 3 deletions src/app/qgsrasterlayerproperties.cpp
Expand Up @@ -1372,7 +1372,15 @@ void QgsRasterLayerProperties::pixelSelected( const QgsPoint& canvasPoint )
if ( mMapCanvas && mPixelSelectorTool )
{
mMapCanvas->unsetMapTool( mPixelSelectorTool );
QMap< int, void *> myPixelMap = mRasterLayer->dataProvider()->identify( mMapCanvas->mapRenderer()->mapToLayerCoordinates( mRasterLayer, canvasPoint ) );

QgsPoint myPoint = mMapCanvas->mapRenderer()->mapToLayerCoordinates( mRasterLayer, canvasPoint );

QgsRectangle myExtent = mMapCanvas->mapRenderer()->mapToLayerCoordinates( mRasterLayer, mMapCanvas->extent() );
double mapUnitsPerPixel = mMapCanvas->mapUnitsPerPixel();
int myWidth = mMapCanvas->extent().width() / mapUnitsPerPixel;
int myHeight = mMapCanvas->extent().height() / mapUnitsPerPixel;

QMap<int, QVariant> myPixelMap = mRasterLayer->dataProvider()->identify( myPoint, QgsRasterDataProvider::IdentifyFormatValue, myExtent, myWidth, myHeight );

QList<int> bands = renderer->usesBands();

Expand All @@ -1383,8 +1391,7 @@ void QgsRasterLayerProperties::pixelSelected( const QgsPoint& canvasPoint )
int bandNo = bands.value( i );
if ( myPixelMap.count( bandNo ) == 1 )
{
void * data = myPixelMap.value( bandNo );
double value = provider->readValue( data, provider->dataType( bandNo ), 0 );
double value = myPixelMap.value( bandNo ).toDouble();
QgsDebugMsg( QString( "value = %1" ).arg( value, 0, 'g', 17 ) );

if ( provider->isNoDataValue( bandNo, value ) )
Expand Down
133 changes: 101 additions & 32 deletions src/core/raster/qgsrasterdataprovider.cpp
Expand Up @@ -22,6 +22,7 @@
#include <QTime>
#include <QMap>
#include <QByteArray>
#include <QVariant>

#include <qmath.h>

Expand Down Expand Up @@ -308,53 +309,121 @@ QString QgsRasterDataProvider::capabilitiesString() const
return abilitiesList.join( ", " );
}

#if 0
bool QgsRasterDataProvider::identify( const QgsPoint& thePoint, QMap<QString, QString>& theResults )
// Default implementation for values
QMap<int, QVariant> QgsRasterDataProvider::identify( const QgsPoint & thePoint, IdentifyFormat theFormat, const QgsRectangle &theExtent, int theWidth, int theHeight )
{
Q_UNUSED( thePoint );
theResults.clear();
return false;
}
QgsDebugMsg( "Entered" );
QMap<int, QVariant> results;

bool QgsRasterDataProvider::identify( const QgsPoint & point, QMap<int, QString>& results )
{
Q_UNUSED( point );
results.clear();
return false;
}
#endif
if ( theFormat != IdentifyFormatValue || !( capabilities() & IdentifyValue ) )
{
QgsDebugMsg( "Format not supported" );
return results;
}

QMap<int, void *> QgsRasterDataProvider::identify( const QgsPoint & point )
{
QMap<int, void *> results;
if ( !extent().contains( thePoint ) )
{
// Outside the raster
for ( int bandNo = 1; bandNo <= bandCount(); bandNo++ )
{
results.insert( bandNo, noDataValue( bandNo ) );
}
return results;
}

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

if ( theWidth == 0 )
{
theWidth = capabilities() & Size ? xSize() : 1000;
}
if ( theHeight == 0 )
{
theHeight = capabilities() & Size ? ySize() : 1000;
}

// Calculate the row / column where the point falls
double xres = ( myExtent.width() ) / theWidth;
double yres = ( myExtent.height() ) / theHeight;

int col = ( int ) floor(( thePoint.x() - myExtent.xMinimum() ) / xres );
int row = ( int ) floor(( myExtent.yMaximum() - thePoint.y() ) / yres );

QgsRectangle myExtent = extent();
double xMin = myExtent.xMinimum() + col * xres;
double xMax = xMin + xres;
double yMax = myExtent.yMaximum() - row * yres;
double yMin = yMax - yres;
QgsRectangle pixelExtent( xMin, yMin, xMax, yMax );

for ( int i = 1; i <= bandCount(); i++ )
{
double x = point.x();
double y = point.y();
QgsRasterBlock * myBlock = block( i, pixelExtent, 1, 1 );

// Calculate the row / column where the point falls
double xres = ( myExtent.xMaximum() - myExtent.xMinimum() ) / xSize();
double yres = ( myExtent.yMaximum() - myExtent.yMinimum() ) / ySize();
double value = noDataValue( i );
if ( myBlock ) value = myBlock->value( 0 );

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

int col = ( int ) floor(( x - myExtent.xMinimum() ) / xres );
int row = ( int ) floor(( myExtent.yMaximum() - y ) / yres );
QMap<QString, QString> QgsRasterDataProvider::identify( const QgsPoint & thePoint, const QgsRectangle &theExtent, int theWidth, int theHeight )
{
QMap<QString, QString> results;

double xMin = myExtent.xMinimum() + col * xres;
double xMax = xMin + xres;
double yMax = myExtent.yMaximum() - row * yres;
double yMin = yMax - yres;
QgsRectangle pixelExtent( xMin, yMin, xMax, yMax );
QgsRasterDataProvider::IdentifyFormat identifyFormat;
if ( capabilities() & QgsRasterDataProvider::IdentifyValue )
{
identifyFormat = QgsRasterDataProvider::IdentifyFormatValue;
}
else if ( capabilities() & QgsRasterDataProvider::IdentifyHtml )
{
identifyFormat = QgsRasterDataProvider::IdentifyFormatHtml;
}
else if ( capabilities() & QgsRasterDataProvider::IdentifyText )
{
identifyFormat = QgsRasterDataProvider::IdentifyFormatText;
}
else
{
return results;
}

void * data = block( i, pixelExtent, 1, 2 );
QMap<int, QVariant> myResults = identify( thePoint, identifyFormat, theExtent, theWidth, theHeight );

if ( data )
if ( identifyFormat == QgsRasterDataProvider::IdentifyFormatValue )
{
foreach ( int bandNo, myResults.keys() )
{
results.insert( i, data );
double value = myResults.value( bandNo ).toDouble();
QString valueString;
if ( isNoDataValue( bandNo, value ) )
{
valueString = tr( "no data" );
}
else
{
valueString = QgsRasterBlock::printValue( value );
}
results.insert( generateBandName( bandNo ), valueString );
}
}
else // text or html
{
foreach ( int bandNo, myResults.keys() )
{
QString value = myResults.value( bandNo ).toString();
// TODO: better 'attribute' name, in theory it may be something else than WMS
// feature info
if ( identifyFormat == QgsRasterDataProvider::IdentifyFormatText )
{
value = "<pre>" + value + "</pre>";
}
results.insert( tr( "Feature info" ), value );
}
}

return results;
}

Expand Down

0 comments on commit df85492

Please sign in to comment.