bool QgsMapToolIdentify::identifyRasterLayer( QgsRasterLayer *layer, int x, int y ) { bool res = true; if ( !layer ) return false; QgsRasterDataProvider *dprovider = layer->dataProvider(); if ( dprovider && ( dprovider->capabilities() & QgsRasterDataProvider::Identify ) == 0 ) return false; QMap< QString, QString > attributes, derivedAttributes; QgsPoint idPoint = mCanvas->getCoordinateTransform()->toMapCoordinates( x, y ); idPoint = toLayerCoordinates( layer, idPoint ); QString type; if ( layer->providerType() == "wms" ) { type = tr( "WMS layer" ); //if WMS layer does not cover the view origin, //we need to map the view pixel coordinates //to WMS layer pixel coordinates QgsRectangle viewExtent = mCanvas->extent(); QgsRectangle layerExtent = layer->extent(); double mapUnitsPerPixel = mCanvas->mapUnitsPerPixel(); if ( mapUnitsPerPixel > 0 && viewExtent.intersects( layerExtent ) ) { double xMinView = viewExtent.xMinimum(); double yMaxView = viewExtent.yMaximum(); double xMinLayer = layerExtent.xMinimum(); double yMaxLayer = layerExtent.yMaximum(); idPoint.set( xMinView < xMinLayer ? floor( x - ( xMinLayer - xMinView ) / mapUnitsPerPixel ) : x, yMaxView > yMaxLayer ? floor( y - ( yMaxView - yMaxLayer ) / mapUnitsPerPixel ) : y ); attributes.insert( tr( "Feature info" ), layer->identifyAsHtml( idPoint ) ); } else { res = false; } } else { type = tr( "Raster" ); res = layer->extent().contains( idPoint ) && layer->identify( idPoint, attributes ); } if ( res ) { derivedAttributes.insert( tr( "(clicked coordinate)" ), idPoint.toString() ); results()->addFeature( layer, type, attributes, derivedAttributes ); } return res; }
bool QgsMapToolIdentify::identifyRasterLayer( QList<IdentifyResult> *results, QgsRasterLayer *layer, QgsPoint point, const QgsRectangle& viewExtent, double mapUnitsPerPixel ) { QgsDebugMsg( "point = " + point.toString() ); if ( !layer ) return false; QgsRasterDataProvider *dprovider = layer->dataProvider(); if ( !dprovider ) return false; int capabilities = dprovider->capabilities(); if ( !( capabilities & QgsRasterDataProvider::Identify ) ) return false; QgsPoint pointInCanvasCrs = point; try { point = toLayerCoordinates( layer, point ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); QgsDebugMsg( QString( "coordinate not reprojectable: %1" ).arg( cse.what() ) ); return false; } QgsDebugMsg( QString( "point = %1 %2" ).arg( point.x() ).arg( point.y() ) ); if ( !layer->extent().contains( point ) ) return false; QMap< QString, QString > attributes, derivedAttributes; QgsRaster::IdentifyFormat format = QgsRasterDataProvider::identifyFormatFromName( layer->customProperty( "identify/format" ).toString() ); // check if the format is really supported otherwise use first supported format if ( !( QgsRasterDataProvider::identifyFormatToCapability( format ) & capabilities ) ) { if ( capabilities & QgsRasterInterface::IdentifyFeature ) format = QgsRaster::IdentifyFormatFeature; else if ( capabilities & QgsRasterInterface::IdentifyValue ) format = QgsRaster::IdentifyFormatValue; else if ( capabilities & QgsRasterInterface::IdentifyHtml ) format = QgsRaster::IdentifyFormatHtml; else if ( capabilities & QgsRasterInterface::IdentifyText ) format = QgsRaster::IdentifyFormatText; else return false; } QgsRasterIdentifyResult identifyResult; // We can only use current map canvas context (extent, width, height) if layer is not reprojected, if ( mCanvas->hasCrsTransformEnabled() && dprovider->crs() != mCanvas->mapSettings().destinationCrs() ) { // To get some reasonable response for point/line WMS vector layers we must // use a context with approximately a resolution in layer CRS units // corresponding to current map canvas resolution (for examplei UMN Mapserver // in msWMSFeatureInfo() -> msQueryByRect() is using requested pixel // + TOLERANCE (layer param) for feature selection) // QgsRectangle r; r.setXMinimum( pointInCanvasCrs.x() - mapUnitsPerPixel / 2. ); r.setXMaximum( pointInCanvasCrs.x() + mapUnitsPerPixel / 2. ); r.setYMinimum( pointInCanvasCrs.y() - mapUnitsPerPixel / 2. ); r.setYMaximum( pointInCanvasCrs.y() + mapUnitsPerPixel / 2. ); r = toLayerCoordinates( layer, r ); // will be a bit larger // Mapserver (6.0.3, for example) does not work with 1x1 pixel box // but that is fixed (the rect is enlarged) in the WMS provider identifyResult = dprovider->identify( point, format, r, 1, 1 ); } else { // It would be nice to use the same extent and size which was used for drawing, // so that WCS can use cache from last draw, unfortunately QgsRasterLayer::draw() // is doing some tricks with extent and size to allign raster to output which // would be difficult to replicate here. // Note: cutting the extent may result in slightly different x and y resolutions // and thus shifted point calculated back in QGIS WMS (using average resolution) //viewExtent = dprovider->extent().intersect( &viewExtent ); // 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. // TODO: may be very dangerous, because it may result in different resolutions // in source CRS, and WMS server (QGIS server) calcs wrong coor using average resolution. int width = qRound( viewExtent.width() / mapUnitsPerPixel ); int height = qRound( viewExtent.height() / mapUnitsPerPixel ); QgsDebugMsg( QString( "viewExtent.width = %1 viewExtent.height = %2" ).arg( viewExtent.width() ).arg( viewExtent.height() ) ); QgsDebugMsg( QString( "width = %1 height = %2" ).arg( width ).arg( height ) ); QgsDebugMsg( QString( "xRes = %1 yRes = %2 mapUnitsPerPixel = %3" ).arg( viewExtent.width() / width ).arg( viewExtent.height() / height ).arg( mapUnitsPerPixel ) ); identifyResult = dprovider->identify( point, format, viewExtent, width, height ); } derivedAttributes.insert( tr( "(clicked coordinate)" ), point.toString() ); if ( identifyResult.isValid() ) { QMap<int, QVariant> values = identifyResult.results(); QgsGeometry geometry; if ( format == QgsRaster::IdentifyFormatValue ) { Q_FOREACH ( int bandNo, values.keys() ) { QString valueString; if ( values.value( bandNo ).isNull() ) { valueString = tr( "no data" ); } else { double value = values.value( bandNo ).toDouble(); valueString = QgsRasterBlock::printValue( value ); } attributes.insert( dprovider->generateBandName( bandNo ), valueString ); } QString label = layer->name(); results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) ); }
ProjectorData::ProjectorData( const QgsRectangle &extent, int width, int height, QgsRasterInterface *input, const QgsCoordinateTransform &inverseCt, QgsRasterProjector::Precision precision ) : mApproximate( false ) , mInverseCt( inverseCt ) , mDestExtent( extent ) , mDestRows( height ) , mDestCols( width ) , mDestXRes( 0.0 ) , mDestYRes( 0.0 ) , mSrcRows( 0 ) , mSrcCols( 0 ) , mSrcXRes( 0.0 ) , mSrcYRes( 0.0 ) , mDestRowsPerMatrixRow( 0.0 ) , mDestColsPerMatrixCol( 0.0 ) , mHelperTopRow( 0 ) , mCPCols( 0 ) , mCPRows( 0 ) , mSqrTolerance( 0.0 ) , mMaxSrcXRes( 0 ) , mMaxSrcYRes( 0 ) { QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 ); // Get max source resolution and extent if possible if ( input ) { QgsRasterDataProvider *provider = dynamic_cast<QgsRasterDataProvider *>( input->sourceInput() ); if ( provider ) { if ( provider->capabilities() & QgsRasterDataProvider::Size ) { mMaxSrcXRes = provider->extent().width() / provider->xSize(); mMaxSrcYRes = provider->extent().height() / provider->ySize(); } // Get source extent if ( mExtent.isEmpty() ) { mExtent = provider->extent(); } } } mDestXRes = mDestExtent.width() / ( mDestCols ); mDestYRes = mDestExtent.height() / ( mDestRows ); // Calculate tolerance // TODO: Think it over better // Note: we are checking on matrix each even point, that means that the real error // in that moment is approximately half size double myDestRes = mDestXRes < mDestYRes ? mDestXRes : mDestYRes; mSqrTolerance = myDestRes * myDestRes; if ( precision == QgsRasterProjector::Approximate ) { mApproximate = true; } else { mApproximate = false; } // Always try to calculate mCPMatrix, it is used in calcSrcExtent() for both Approximate and Exact // Initialize the matrix by corners and middle points mCPCols = mCPRows = 3; for ( int i = 0; i < mCPRows; i++ ) { QList<QgsPointXY> myRow; myRow.append( QgsPointXY() ); myRow.append( QgsPointXY() ); myRow.append( QgsPointXY() ); mCPMatrix.insert( i, myRow ); // And the legal points QList<bool> myLegalRow; myLegalRow.append( bool( false ) ); myLegalRow.append( bool( false ) ); myLegalRow.append( bool( false ) ); mCPLegalMatrix.insert( i, myLegalRow ); } for ( int i = 0; i < mCPRows; i++ ) { calcRow( i, inverseCt ); } while ( true ) { bool myColsOK = checkCols( inverseCt ); if ( !myColsOK ) { insertRows( inverseCt ); } bool myRowsOK = checkRows( inverseCt ); if ( !myRowsOK ) { insertCols( inverseCt ); } if ( myColsOK && myRowsOK ) { QgsDebugMsgLevel( QStringLiteral( "CP matrix within tolerance" ), 4 ); break; } // What is the maximum reasonable size of transformatio matrix? // TODO: consider better when to break - ratio if ( mCPRows * mCPCols > 0.25 * mDestRows * mDestCols ) //if ( mCPRows * mCPCols > mDestRows * mDestCols ) { QgsDebugMsgLevel( QStringLiteral( "Too large CP matrix" ), 4 ); mApproximate = false; break; } } QgsDebugMsgLevel( QStringLiteral( "CPMatrix size: mCPRows = %1 mCPCols = %2" ).arg( mCPRows ).arg( mCPCols ), 4 ); mDestRowsPerMatrixRow = static_cast< float >( mDestRows ) / ( mCPRows - 1 ); mDestColsPerMatrixCol = static_cast< float >( mDestCols ) / ( mCPCols - 1 ); QgsDebugMsgLevel( QStringLiteral( "CPMatrix:" ), 5 ); QgsDebugMsgLevel( cpToString(), 5 ); // init helper points pHelperTop = new QgsPointXY[mDestCols]; pHelperBottom = new QgsPointXY[mDestCols]; calcHelper( 0, pHelperTop ); calcHelper( 1, pHelperBottom ); mHelperTopRow = 0; // Calculate source dimensions calcSrcExtent(); calcSrcRowsCols(); mSrcYRes = mSrcExtent.height() / mSrcRows; mSrcXRes = mSrcExtent.width() / mSrcCols; }
QgsRasterBlock* QgsRasterResampleFilter::block2( int bandNo, QgsRectangle const & extent, int width, int height, QgsRasterBlockFeedback *feedback ) { Q_UNUSED( bandNo ); QgsDebugMsg( QString( "width = %1 height = %2 extent = %3" ).arg( width ).arg( height ).arg( extent.toString() ) ); QgsRasterBlock *outputBlock = new QgsRasterBlock(); if ( !mInput ) return outputBlock; double oversampling = 1.0; // approximate global oversampling factor if ( mZoomedInResampler || mZoomedOutResampler ) { QgsRasterDataProvider *provider = dynamic_cast<QgsRasterDataProvider*>( mInput->srcInput() ); if ( provider && ( provider->capabilities() & QgsRasterDataProvider::Size ) ) { double xRes = extent.width() / width; double providerXRes = provider->extent().width() / provider->xSize(); double pixelRatio = xRes / providerXRes; oversampling = ( pixelRatio > mMaxOversampling ) ? mMaxOversampling : pixelRatio; QgsDebugMsg( QString( "xRes = %1 providerXRes = %2 pixelRatio = %3 oversampling = %4" ).arg( xRes ).arg( providerXRes ).arg( pixelRatio ).arg( oversampling ) ); } else { // We don't know exact data source resolution (WMS) so we expect that // server data have higher resolution (which is not always true) and use // mMaxOversampling oversampling = mMaxOversampling; } } QgsDebugMsg( QString( "oversampling %1" ).arg( oversampling ) ); int bandNumber = 1; // Do no oversampling if no resampler for zoomed in / zoomed out (nearest neighbour) // We do mZoomedInResampler if oversampling == 1 (otherwise for example reprojected // zoom in rasters are never resampled because projector limits resolution. if ((( oversampling < 1.0 || qgsDoubleNear( oversampling, 1.0 ) ) && !mZoomedInResampler ) || ( oversampling > 1.0 && !mZoomedOutResampler ) ) { QgsDebugMsg( "No oversampling." ); delete outputBlock; return mInput->block2( bandNumber, extent, width, height, feedback ); } //effective oversampling factors are different to global one because of rounding double oversamplingX = (( double )width * oversampling ) / width; double oversamplingY = (( double )height * oversampling ) / height; // TODO: we must also increase the extent to get correct result on borders of parts int resWidth = width * oversamplingX; int resHeight = height * oversamplingY; QgsRasterBlock *inputBlock = mInput->block2( bandNumber, extent, resWidth, resHeight, feedback ); if ( !inputBlock || inputBlock->isEmpty() ) { QgsDebugMsg( "No raster data!" ); delete inputBlock; return outputBlock; } if ( !outputBlock->reset( QGis::ARGB32_Premultiplied, width, height ) ) { delete inputBlock; return outputBlock; } //resample image QImage img = inputBlock->image(); QImage dstImg = QImage( width, height, QImage::Format_ARGB32_Premultiplied ); if ( mZoomedInResampler && ( oversamplingX < 1.0 || qgsDoubleNear( oversampling, 1.0 ) ) ) { QgsDebugMsg( "zoomed in resampling" ); mZoomedInResampler->resample( img, dstImg ); } else if ( mZoomedOutResampler && oversamplingX > 1.0 ) { QgsDebugMsg( "zoomed out resampling" ); mZoomedOutResampler->resample( img, dstImg ); } else { // Should not happen QgsDebugMsg( "Unexpected resampling" ); dstImg = img.scaled( width, height ); } outputBlock->setImage( &dstImg ); delete inputBlock; return outputBlock; // No resampling }
QgsRasterBlock * QgsRasterResampleFilter::block( int bandNo, QgsRectangle const & extent, int width, int height ) { Q_UNUSED( bandNo ); QgsDebugMsg( "Entered" ); QgsRasterBlock *outputBlock = new QgsRasterBlock(); if ( !mInput ) return outputBlock; double oversampling = 1.0; // approximate global oversampling factor if ( mZoomedInResampler || mZoomedOutResampler ) { QgsRasterDataProvider *provider = dynamic_cast<QgsRasterDataProvider*>( mInput->srcInput() ); // Do not oversample if data source does not have fixed resolution (WMS) if ( provider && ( provider->capabilities() & QgsRasterDataProvider::ExactResolution ) ) { double xRes = extent.width() / width; double providerXRes = provider->extent().width() / provider->xSize(); double pixelRatio = xRes / providerXRes; oversampling = ( pixelRatio > mMaxOversampling ) ? mMaxOversampling : pixelRatio; QgsDebugMsg( QString( "xRes = %1 providerXRes = %2 pixelRatio = %3 oversampling = %4" ).arg( xRes ).arg( providerXRes ).arg( pixelRatio ).arg( oversampling ) ); } } //set oversampling back to 1.0 if no resampler for zoomed in / zoomed out (nearest neighbour) if (( oversampling < 1.0 && !mZoomedInResampler ) || ( oversampling > 1.0 && !mZoomedOutResampler ) ) { oversampling = 1.0; } QgsDebugMsg( QString( "oversampling %1" ).arg( oversampling ) ); //effective oversampling factors are different to global one because of rounding double oversamplingX = (( double )width * oversampling ) / width; double oversamplingY = (( double )height * oversampling ) / height; // TODO: we must also increase the extent to get correct result on borders of parts int resWidth = width * oversamplingX; int resHeight = height * oversamplingY; // At moment we know that we read rendered image int bandNumber = 1; //void *rasterData = mInput->block( bandNumber, extent, resWidth, resHeight ); QgsRasterBlock *inputBlock = mInput->block( bandNumber, extent, resWidth, resHeight ); if ( !inputBlock || inputBlock->isEmpty() ) { QgsDebugMsg( "No raster data!" ); delete inputBlock; return outputBlock; } if ( doubleNear( oversamplingX, 1.0 ) || doubleNear( oversamplingY, 1.0 ) ) { QgsDebugMsg( "No oversampling." ); delete outputBlock; return inputBlock; } if ( !outputBlock->reset( QgsRasterBlock::ARGB32_Premultiplied, width, height ) ) { delete inputBlock; return outputBlock; } //resample image QImage img = inputBlock->image(); QImage dstImg = QImage( width, height, QImage::Format_ARGB32_Premultiplied ); if ( mZoomedInResampler && oversamplingX < 1.0 ) { QgsDebugMsg( "zoomed in resampling" ); mZoomedInResampler->resample( img, dstImg ); } else if ( mZoomedOutResampler && oversamplingX > 1.0 ) { QgsDebugMsg( "zoomed out resampling" ); mZoomedOutResampler->resample( img, dstImg ); } else { // Should not happen QgsDebugMsg( "Unexpected resampling" ); dstImg = img.scaled( width, height ); } outputBlock->setImage( &dstImg ); delete inputBlock; return outputBlock; // No resampling }
void QgsRasterProjector::calc() { QgsDebugMsg( "Entered" ); mCPMatrix.clear(); mCPLegalMatrix.clear(); delete[] pHelperTop; pHelperTop = 0; delete[] pHelperBottom; pHelperBottom = 0; // Get max source resolution and extent if possible mMaxSrcXRes = 0; mMaxSrcYRes = 0; if ( mInput ) { QgsRasterDataProvider *provider = dynamic_cast<QgsRasterDataProvider*>( mInput->srcInput() ); if ( provider && ( provider->capabilities() & QgsRasterDataProvider::Size ) ) { mMaxSrcXRes = provider->extent().width() / provider->xSize(); mMaxSrcYRes = provider->extent().height() / provider->ySize(); } // Get source extent if ( mExtent.isEmpty() ) { mExtent = provider->extent(); } } mDestXRes = mDestExtent.width() / ( mDestCols ); mDestYRes = mDestExtent.height() / ( mDestRows ); // Calculate tolerance // TODO: Think it over better // Note: we are checking on matrix each even point, that means that the real error // in that moment is approximately half size double myDestRes = mDestXRes < mDestYRes ? mDestXRes : mDestYRes; mSqrTolerance = myDestRes * myDestRes; const QgsCoordinateTransform* ct = QgsCoordinateTransformCache::instance()->transform( mDestCRS.authid(), mSrcCRS.authid(), mDestDatumTransform, mSrcDatumTransform ); // Initialize the matrix by corners and middle points mCPCols = mCPRows = 3; for ( int i = 0; i < mCPRows; i++ ) { QList<QgsPoint> myRow; myRow.append( QgsPoint() ); myRow.append( QgsPoint() ); myRow.append( QgsPoint() ); mCPMatrix.insert( i, myRow ); // And the legal points QList<bool> myLegalRow; myLegalRow.append( bool( false ) ); myLegalRow.append( bool( false ) ); myLegalRow.append( bool( false ) ); mCPLegalMatrix.insert( i, myLegalRow ); } for ( int i = 0; i < mCPRows; i++ ) { calcRow( i, ct ); } while ( true ) { bool myColsOK = checkCols( ct ); if ( !myColsOK ) { insertRows( ct ); } bool myRowsOK = checkRows( ct ); if ( !myRowsOK ) { insertCols( ct ); } if ( myColsOK && myRowsOK ) { QgsDebugMsg( "CP matrix within tolerance" ); mApproximate = true; break; } // What is the maximum reasonable size of transformatio matrix? // TODO: consider better when to break - ratio if ( mCPRows * mCPCols > 0.25 * mDestRows * mDestCols ) { QgsDebugMsg( "Too large CP matrix" ); mApproximate = false; break; } } QgsDebugMsg( QString( "CPMatrix size: mCPRows = %1 mCPCols = %2" ).arg( mCPRows ).arg( mCPCols ) ); mDestRowsPerMatrixRow = ( float )mDestRows / ( mCPRows - 1 ); mDestColsPerMatrixCol = ( float )mDestCols / ( mCPCols - 1 ); QgsDebugMsgLevel( "CPMatrix:", 5 ); QgsDebugMsgLevel( cpToString(), 5 ); // Calculate source dimensions calcSrcExtent(); calcSrcRowsCols(); mSrcYRes = mSrcExtent.height() / mSrcRows; mSrcXRes = mSrcExtent.width() / mSrcCols; // init helper points pHelperTop = new QgsPoint[mDestCols]; pHelperBottom = new QgsPoint[mDestCols]; calcHelper( 0, pHelperTop ); calcHelper( 1, pHelperBottom ); mHelperTopRow = 0; }
bool QgsMapToolIdentify::identifyRasterLayer( QList<IdentifyResult> *results, QgsRasterLayer *layer, QgsPointXY point, const QgsRectangle &viewExtent, double mapUnitsPerPixel ) { QgsDebugMsg( "point = " + point.toString() ); if ( !layer ) return false; QgsRasterDataProvider *dprovider = layer->dataProvider(); if ( !dprovider ) return false; int capabilities = dprovider->capabilities(); if ( !( capabilities & QgsRasterDataProvider::Identify ) ) return false; QgsPointXY pointInCanvasCrs = point; try { point = toLayerCoordinates( layer, point ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); QgsDebugMsg( QString( "coordinate not reprojectable: %1" ).arg( cse.what() ) ); return false; } QgsDebugMsg( QString( "point = %1 %2" ).arg( point.x() ).arg( point.y() ) ); if ( !layer->extent().contains( point ) ) return false; QMap< QString, QString > attributes, derivedAttributes; QgsRaster::IdentifyFormat format = QgsRasterDataProvider::identifyFormatFromName( layer->customProperty( QStringLiteral( "identify/format" ) ).toString() ); // check if the format is really supported otherwise use first supported format if ( !( QgsRasterDataProvider::identifyFormatToCapability( format ) & capabilities ) ) { if ( capabilities & QgsRasterInterface::IdentifyFeature ) format = QgsRaster::IdentifyFormatFeature; else if ( capabilities & QgsRasterInterface::IdentifyValue ) format = QgsRaster::IdentifyFormatValue; else if ( capabilities & QgsRasterInterface::IdentifyHtml ) format = QgsRaster::IdentifyFormatHtml; else if ( capabilities & QgsRasterInterface::IdentifyText ) format = QgsRaster::IdentifyFormatText; else return false; } QgsRasterIdentifyResult identifyResult; // We can only use current map canvas context (extent, width, height) if layer is not reprojected, if ( dprovider->crs() != mCanvas->mapSettings().destinationCrs() ) { // To get some reasonable response for point/line WMS vector layers we must // use a context with approximately a resolution in layer CRS units // corresponding to current map canvas resolution (for examplei UMN Mapserver // in msWMSFeatureInfo() -> msQueryByRect() is using requested pixel // + TOLERANCE (layer param) for feature selection) // QgsRectangle r; r.setXMinimum( pointInCanvasCrs.x() - mapUnitsPerPixel / 2. ); r.setXMaximum( pointInCanvasCrs.x() + mapUnitsPerPixel / 2. ); r.setYMinimum( pointInCanvasCrs.y() - mapUnitsPerPixel / 2. ); r.setYMaximum( pointInCanvasCrs.y() + mapUnitsPerPixel / 2. ); r = toLayerCoordinates( layer, r ); // will be a bit larger // Mapserver (6.0.3, for example) does not work with 1x1 pixel box // but that is fixed (the rect is enlarged) in the WMS provider identifyResult = dprovider->identify( point, format, r, 1, 1 ); } else { // It would be nice to use the same extent and size which was used for drawing, // so that WCS can use cache from last draw, unfortunately QgsRasterLayer::draw() // is doing some tricks with extent and size to align raster to output which // would be difficult to replicate here. // Note: cutting the extent may result in slightly different x and y resolutions // and thus shifted point calculated back in QGIS WMS (using average resolution) //viewExtent = dprovider->extent().intersect( &viewExtent ); // 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. // TODO: may be very dangerous, because it may result in different resolutions // in source CRS, and WMS server (QGIS server) calcs wrong coor using average resolution. int width = std::round( viewExtent.width() / mapUnitsPerPixel ); int height = std::round( viewExtent.height() / mapUnitsPerPixel ); QgsDebugMsg( QString( "viewExtent.width = %1 viewExtent.height = %2" ).arg( viewExtent.width() ).arg( viewExtent.height() ) ); QgsDebugMsg( QString( "width = %1 height = %2" ).arg( width ).arg( height ) ); QgsDebugMsg( QString( "xRes = %1 yRes = %2 mapUnitsPerPixel = %3" ).arg( viewExtent.width() / width ).arg( viewExtent.height() / height ).arg( mapUnitsPerPixel ) ); identifyResult = dprovider->identify( point, format, viewExtent, width, height ); } derivedAttributes.insert( tr( "(clicked coordinate X)" ), formatXCoordinate( pointInCanvasCrs ) ); derivedAttributes.insert( tr( "(clicked coordinate Y)" ), formatYCoordinate( pointInCanvasCrs ) ); if ( identifyResult.isValid() ) { QMap<int, QVariant> values = identifyResult.results(); QgsGeometry geometry; if ( format == QgsRaster::IdentifyFormatValue ) { for ( auto it = values.constBegin(); it != values.constEnd(); ++it ) { QString valueString; if ( it.value().isNull() ) { valueString = tr( "no data" ); } else { QVariant value( it.value() ); // The cast is legit. Quoting QT doc : // "Although this function is declared as returning QVariant::Type, // the return value should be interpreted as QMetaType::Type" if ( static_cast<QMetaType::Type>( value.type() ) == QMetaType::Float ) { valueString = QgsRasterBlock::printValue( value.toFloat() ); } else { valueString = QgsRasterBlock::printValue( value.toDouble() ); } } attributes.insert( dprovider->generateBandName( it.key() ), valueString ); } QString label = layer->name(); results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) ); } else if ( format == QgsRaster::IdentifyFormatFeature ) { for ( auto it = values.constBegin(); it != values.constEnd(); ++it ) { QVariant value = it.value(); if ( value.type() == QVariant::Bool && !value.toBool() ) { // sublayer not visible or not queryable continue; } if ( value.type() == QVariant::String ) { // error // TODO: better error reporting QString label = layer->subLayers().value( it.key() ); attributes.clear(); attributes.insert( tr( "Error" ), value.toString() ); results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) ); continue; } // list of feature stores for a single sublayer const QgsFeatureStoreList featureStoreList = it.value().value<QgsFeatureStoreList>(); for ( const QgsFeatureStore &featureStore : featureStoreList ) { const QgsFeatureList storeFeatures = featureStore.features(); for ( QgsFeature feature : storeFeatures ) { attributes.clear(); // WMS sublayer and feature type, a sublayer may contain multiple feature types. // Sublayer name may be the same as layer name and feature type name // may be the same as sublayer. We try to avoid duplicities in label. QString sublayer = featureStore.params().value( QStringLiteral( "sublayer" ) ).toString(); QString featureType = featureStore.params().value( QStringLiteral( "featureType" ) ).toString(); // Strip UMN MapServer '_feature' featureType.remove( QStringLiteral( "_feature" ) ); QStringList labels; if ( sublayer.compare( layer->name(), Qt::CaseInsensitive ) != 0 ) { labels << sublayer; } if ( featureType.compare( sublayer, Qt::CaseInsensitive ) != 0 || labels.isEmpty() ) { labels << featureType; } QMap< QString, QString > derAttributes = derivedAttributes; derAttributes.unite( featureDerivedAttributes( &feature, layer ) ); IdentifyResult identifyResult( qobject_cast<QgsMapLayer *>( layer ), labels.join( QStringLiteral( " / " ) ), featureStore.fields(), feature, derAttributes ); identifyResult.mParams.insert( QStringLiteral( "getFeatureInfoUrl" ), featureStore.params().value( QStringLiteral( "getFeatureInfoUrl" ) ) ); results->append( identifyResult ); } } } } else // text or html { QgsDebugMsg( QString( "%1 HTML or text values" ).arg( values.size() ) ); for ( auto it = values.constBegin(); it != values.constEnd(); ++it ) { QString value = it.value().toString(); attributes.clear(); attributes.insert( QLatin1String( "" ), value ); QString label = layer->subLayers().value( it.key() ); results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) ); } } } else { attributes.clear(); QString value = identifyResult.error().message( QgsErrorMessage::Text ); attributes.insert( tr( "Error" ), value ); QString label = tr( "Identify error" ); results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) ); } return true; }
bool QgsMapToolIdentify::identifyRasterLayer( QList<IdentifyResult> *results, QgsRasterLayer *layer, QgsPoint point, QgsRectangle viewExtent, double mapUnitsPerPixel ) { QgsDebugMsg( "point = " + point.toString() ); if ( !layer ) return false; QgsRasterDataProvider *dprovider = layer->dataProvider(); int capabilities = dprovider->capabilities(); if ( !dprovider || !( capabilities & QgsRasterDataProvider::Identify ) ) { return false; } try { point = toLayerCoordinates( layer, point ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); QgsDebugMsg( QString( "coordinate not reprojectable: %1" ).arg( cse.what() ) ); return false; } QgsDebugMsg( QString( "point = %1 %2" ).arg( point.x() ).arg( point.y() ) ); if ( !layer->extent().contains( point ) ) return false; QMap< QString, QString > attributes, derivedAttributes; QMap<int, QVariant> values; QgsRasterDataProvider::IdentifyFormat format = QgsRasterDataProvider::identifyFormatFromName( layer->customProperty( "identify/format" ).toString() ); // check if the format is really supported otherwise use first supported format if ( !( QgsRasterDataProvider::identifyFormatToCapability( format ) & capabilities ) ) { if ( capabilities & QgsRasterInterface::IdentifyFeature ) format = QgsRasterDataProvider::IdentifyFormatFeature; else if ( capabilities & QgsRasterInterface::IdentifyValue ) format = QgsRasterDataProvider::IdentifyFormatValue; else if ( capabilities & QgsRasterInterface::IdentifyHtml ) format = QgsRasterDataProvider::IdentifyFormatHtml; else if ( capabilities & QgsRasterInterface::IdentifyText ) format = QgsRasterDataProvider::IdentifyFormatText; else return false; } // We can only use context (extent, width, heigh) if layer is not reprojected, // otherwise we don't know source resolution (size). if ( mCanvas->hasCrsTransformEnabled() && dprovider->crs() != mCanvas->mapRenderer()->destinationCrs() ) { viewExtent = toLayerCoordinates( layer, viewExtent ); values = dprovider->identify( point, format ); } else { // It would be nice to use the same extent and size which was used for drawing, // so that WCS can use cache from last draw, unfortunately QgsRasterLayer::draw() // is doing some tricks with extent and size to allign raster to output which // would be difficult to replicate here. // Note: cutting the extent may result in slightly different x and y resolutions // and thus shifted point calculated back in QGIS WMS (using average resolution) //viewExtent = dprovider->extent().intersect( &viewExtent ); // 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. // TODO: may be very dangerous, because it may result in different resolutions // in source CRS, and WMS server (QGIS server) calcs wrong coor using average resolution. int width = qRound( viewExtent.width() / mapUnitsPerPixel ); int height = qRound( viewExtent.height() / mapUnitsPerPixel ); QgsDebugMsg( QString( "viewExtent.width = %1 viewExtent.height = %2" ).arg( viewExtent.width() ).arg( viewExtent.height() ) ); QgsDebugMsg( QString( "width = %1 height = %2" ).arg( width ).arg( height ) ); QgsDebugMsg( QString( "xRes = %1 yRes = %2 mapUnitsPerPixel = %3" ).arg( viewExtent.width() / width ).arg( viewExtent.height() / height ).arg( mapUnitsPerPixel ) ); values = dprovider->identify( point, format, viewExtent, width, height ); } derivedAttributes.insert( tr( "(clicked coordinate)" ), point.toString() ); //QString type = tr( "Raster" ); QgsGeometry geometry; if ( format == QgsRasterDataProvider::IdentifyFormatValue ) { foreach ( int bandNo, values.keys() ) { double value = values.value( bandNo ).toDouble(); QString valueString; if ( dprovider->isNoDataValue( bandNo, value ) ) { valueString = tr( "no data" ); } else { valueString = QgsRasterBlock::printValue( value ); } attributes.insert( dprovider->generateBandName( bandNo ), valueString ); } QString label = layer->name(); results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) ); }
bool QgsMapToolIdentify::identifyRasterLayer( QgsRasterLayer *layer, int x, int y ) { bool res = true; if ( !layer ) return false; QgsRasterDataProvider *dprovider = layer->dataProvider(); if ( !dprovider || !( dprovider->capabilities() & QgsRasterDataProvider::Identify ) ) { return false; } QgsPoint idPoint = mCanvas->getCoordinateTransform()->toMapCoordinates( x, y ); try { idPoint = toLayerCoordinates( layer, idPoint ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); QgsDebugMsg( QString( "coordinate not reprojectable: %1" ).arg( cse.what() ) ); return false; } QgsDebugMsg( QString( "idPoint = %1 %2" ).arg( idPoint.x() ).arg( idPoint.y() ) ); if ( !layer->extent().contains( idPoint ) ) return false; QgsRectangle viewExtent = mCanvas->extent(); QMap< QString, QString > attributes, derivedAttributes; // We can only use context (extent, width, heigh) if layer is not reprojected, // otherwise we don't know source resolution (size). if ( mCanvas->hasCrsTransformEnabled() && dprovider->crs() != mCanvas->mapRenderer()->destinationCrs() ) { viewExtent = toLayerCoordinates( layer, viewExtent ); attributes = dprovider->identify( idPoint ); } else { // It would be nice to use the same extent and size which was used for drawing, // so that WCS can use cache from last draw, unfortunately QgsRasterLayer::draw() // is doing some tricks with extent and size to allign raster to output which // would be difficult to replicate here. // Note: cutting the extent may result in slightly different x and y resolutions // and thus shifted point calculated back in QGIS WMS (using average resolution) //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. // TODO: may be very dangerous, because it may result in different resolutions // in source CRS, and WMS server (QGIS server) calcs wrong coor using average resolution. int width = qRound( viewExtent.width() / mapUnitsPerPixel ); int height = qRound( viewExtent.height() / mapUnitsPerPixel ); QgsDebugMsg( QString( "viewExtent.width = %1 viewExtent.height = %2" ).arg( viewExtent.width() ).arg( viewExtent.height() ) ); QgsDebugMsg( QString( "width = %1 height = %2" ).arg( width ).arg( height ) ); QgsDebugMsg( QString( "xRes = %1 yRes = %2 mapUnitsPerPixel = %3" ).arg( viewExtent.width() / width ).arg( viewExtent.height() / height ).arg( mapUnitsPerPixel ) ); 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 ); } return res; }