コード例 #1
0
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;
}
コード例 #2
0
ファイル: qgsmaptoolidentify.cpp プロジェクト: nicanor-b/QGIS
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 ) );
    }
コード例 #3
0
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;
}
コード例 #4
0
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
}
コード例 #5
0
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
}
コード例 #6
0
ファイル: qgsrasterprojector.cpp プロジェクト: nbozon/QGIS
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;
}
コード例 #7
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;
}
コード例 #8
0
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 ) );
  }
コード例 #9
0
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;
}