Esempio n. 1
0
bool QgsRasterChecker::runTest( QString theVerifiedKey, QString theVerifiedUri,
                                QString theExpectedKey, QString theExpectedUri )
{
  bool ok = true;
  mReport += "\n\n";

  //QgsRasterDataProvider* verifiedProvider = QgsRasterLayer::loadProvider( theVerifiedKey, theVerifiedUri );
  QgsRasterDataProvider* verifiedProvider = ( QgsRasterDataProvider* ) QgsProviderRegistry::instance()->provider( theVerifiedKey, theVerifiedUri );
  if ( !verifiedProvider || !verifiedProvider->isValid() )
  {
    error( QString( "Cannot load provider %1 with URI: %2" ).arg( theVerifiedKey ).arg( theVerifiedUri ), mReport );
    ok = false;
  }

  //QgsRasterDataProvider* expectedProvider = QgsRasterLayer::loadProvider( theExpectedKey, theExpectedUri );
  QgsRasterDataProvider* expectedProvider = ( QgsRasterDataProvider* ) QgsProviderRegistry::instance()->provider( theExpectedKey, theExpectedUri );
  if ( !expectedProvider || !expectedProvider->isValid() )
  {
    error( QString( "Cannot load provider %1 with URI: %2" ).arg( theExpectedKey ).arg( theExpectedUri ), mReport );
    ok = false;
  }

  if ( !ok ) return false;

  mReport += QString( "Verified URI: %1<br>" ).arg( theVerifiedUri.replace( "&", "&amp;" ) );
  mReport += QString( "Expected URI: %1<br>" ).arg( theExpectedUri.replace( "&", "&amp;" ) );

  mReport += "<br>";
  mReport += QString( "<table style='%1'>\n" ).arg( mTabStyle );
  mReport += compareHead();

  compare( "Band count", verifiedProvider->bandCount(), expectedProvider->bandCount(), mReport, ok );

  compare( "Width", verifiedProvider->xSize(), expectedProvider->xSize(), mReport, ok );
  compare( "Height", verifiedProvider->ySize(), expectedProvider->ySize(), mReport, ok );

  compareRow( "Extent", verifiedProvider->extent().toString(), expectedProvider->extent().toString(), mReport, verifiedProvider->extent() == expectedProvider->extent() );

  if ( verifiedProvider->extent() != expectedProvider->extent() ) ok = false;


  mReport += "</table>\n";

  if ( !ok ) return false;

  bool allOk = true;
  for ( int band = 1; band <= expectedProvider->bandCount(); band++ )
  {
    bool bandOk = true;
    mReport += QString( "<h3>Band %1</h3>\n" ).arg( band );
    mReport += QString( "<table style='%1'>\n" ).arg( mTabStyle );
    mReport += compareHead();

    // Data types may differ (?)
    bool typesOk = true;
    compare( "Source data type", verifiedProvider->srcDataType( band ), expectedProvider->srcDataType( band ), mReport, typesOk );
    compare( "Data type", verifiedProvider->dataType( band ), expectedProvider->dataType( band ), mReport, typesOk ) ;

    // TODO: not yet sure if noDataValue() should exist at all
    //compare( "No data (NULL) value", verifiedProvider->noDataValue( band ), expectedProvider->noDataValue( band ), mReport, typesOk );

    bool statsOk = true;
    QgsRasterBandStats verifiedStats =  verifiedProvider->bandStatistics( band );
    QgsRasterBandStats expectedStats =  expectedProvider->bandStatistics( band );

    // Min/max may 'slightly' differ, for big numbers however, the difference may
    // be quite big, for example for Float32 with max -3.332e+38, the difference is 1.47338e+24
    double tol = tolerance( expectedStats.minimumValue );
    compare( "Minimum value", verifiedStats.minimumValue, expectedStats.minimumValue, mReport, statsOk, tol );
    tol = tolerance( expectedStats.maximumValue );
    compare( "Maximum value", verifiedStats.maximumValue, expectedStats.maximumValue, mReport, statsOk, tol );

    // TODO: enable once fixed (WCS excludes nulls but GDAL does not)
    //compare( "Cells count", verifiedStats.elementCount, expectedStats.elementCount, mReport, statsOk );

    tol = tolerance( expectedStats.mean );
    compare( "Mean", verifiedStats.mean, expectedStats.mean, mReport, statsOk, tol );

    // stdDev usually differ significantly
    tol = tolerance( expectedStats.stdDev, 1 );
    compare( "Standard deviation", verifiedStats.stdDev, expectedStats.stdDev, mReport, statsOk, tol );

    mReport += "</table>";
    mReport += "<br>";

    if ( !bandOk )
    {
      allOk = false;
      continue;
    }

    if ( !statsOk || !typesOk )
    {
      allOk = false;
      // create values table anyway so that values are available
    }

    mReport += "<table><tr>";
    mReport += "<td>Data comparison</td>";
    mReport += QString( "<td style='%1 %2 border: 1px solid'>correct&nbsp;value</td>" ).arg( mCellStyle ).arg( mOkStyle );
    mReport += "<td></td>";
    mReport += QString( "<td style='%1 %2 border: 1px solid'>wrong&nbsp;value<br>expected value</td></tr>" ).arg( mCellStyle ).arg( mErrStyle );
    mReport += "</tr></table>";
    mReport += "<br>";

    int width = expectedProvider->xSize();
    int height = expectedProvider->ySize();
    QgsRasterBlock *expectedBlock = expectedProvider->block( band, expectedProvider->extent(), width, height );
    QgsRasterBlock *verifiedBlock = verifiedProvider->block( band, expectedProvider->extent(), width, height );

    if ( !expectedBlock || !expectedBlock->isValid() ||
         !verifiedBlock || !verifiedBlock->isValid() )
    {
      allOk = false;
      mReport += "cannot read raster block";
      continue;
    }

    // compare data values
    QString htmlTable = QString( "<table style='%1'>" ).arg( mTabStyle );
    for ( int row = 0; row < height; row ++ )
    {
      htmlTable += "<tr>";
      for ( int col = 0; col < width; col ++ )
      {
        bool cellOk = true;
        double verifiedVal = verifiedBlock->value( row, col );
        double expectedVal = expectedBlock->value( row, col );

        QString valStr;
        if ( compare( verifiedVal, expectedVal, 0 ) )
        {
          valStr = QString( "%1" ).arg( verifiedVal );
        }
        else
        {
          cellOk = false;
          allOk = false;
          valStr = QString( "%1<br>%2" ).arg( verifiedVal ).arg( expectedVal );
        }
        htmlTable += QString( "<td style='%1 %2'>%3</td>" ).arg( mCellStyle ).arg( cellOk ? mOkStyle : mErrStyle ).arg( valStr );
      }
      htmlTable += "</tr>";
    }
    htmlTable += "</table>";

    mReport += htmlTable;

    delete expectedBlock;
    delete verifiedBlock;
  }
  delete verifiedProvider;
  delete expectedProvider;
  return allOk;
}
Esempio n. 2
0
QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster( const QgsRasterPipe* pipe, QgsRasterIterator* iter, int nCols, int nRows, const QgsRectangle& outputExtent,
    const QgsCoordinateReferenceSystem& crs, QProgressDialog* progressDialog )
{
  QgsDebugMsg( "Entered" );
  if ( !iter )
  {
    return SourceProviderError;
  }

  const QgsRasterInterface* iface = pipe->last();
  if ( !iface )
  {
    return SourceProviderError;
  }

  QgsRasterDataProvider* srcProvider = const_cast<QgsRasterDataProvider*>( dynamic_cast<const QgsRasterDataProvider*>( iface->srcInput() ) );
  if ( !srcProvider )
  {
    QgsDebugMsg( "Cannot get source data provider" );
    return SourceProviderError;
  }

  iter->setMaximumTileWidth( mMaxTileWidth );
  iter->setMaximumTileHeight( mMaxTileHeight );

  int nBands = iface->bandCount();
  if ( nBands < 1 )
  {
    return SourceProviderError;
  }


  //check if all the bands have the same data type size, otherwise we cannot write it to the provider
  //(at least not with the current interface)
  int dataTypeSize = QgsRasterBlock::typeSize( srcProvider->srcDataType( 1 ) );
  for ( int i = 2; i <= nBands; ++i )
  {
    if ( QgsRasterBlock::typeSize( srcProvider->srcDataType( 1 ) ) != dataTypeSize )
    {
      return DestProviderError;
    }
  }

  // Output data type - source data type is preferred but it may happen that we need
  // to set 'no data' value (which was not set on source data) if output extent
  // is larger than source extent (with or without reprojection) and there is no 'free'
  // (not used) value available
  QList<bool> destHasNoDataValueList;
  QList<double> destNoDataValueList;
  QList<QGis::DataType> destDataTypeList;
  for ( int bandNo = 1; bandNo <= nBands; bandNo++ )
  {
    QgsRasterNuller *nuller = pipe->nuller();

    bool srcHasNoDataValue = srcProvider->srcHasNoDataValue( bandNo );
    bool destHasNoDataValue = false;
    double destNoDataValue = std::numeric_limits<double>::quiet_NaN();
    QGis::DataType destDataType = srcProvider->srcDataType( bandNo );
    // TODO: verify what happens/should happen if srcNoDataValue is disabled by setUseSrcNoDataValue
    QgsDebugMsg( QString( "srcHasNoDataValue = %1 srcNoDataValue = %2" ).arg( srcHasNoDataValue ).arg( srcProvider->srcNoDataValue( bandNo ) ) );
    if ( srcHasNoDataValue )
    {

      // If source has no data value, it is used by provider
      destNoDataValue = srcProvider->srcNoDataValue( bandNo );
      destHasNoDataValue = true;
    }
    else if ( nuller && nuller->noData( bandNo ).size() > 0 )
    {
      // Use one user defined no data value
      destNoDataValue = nuller->noData( bandNo ).value( 0 ).min();
      destHasNoDataValue = true;
    }
    else
    {
      // Verify if we realy need no data value, i.e.
      QgsRectangle srcExtent = outputExtent;
      QgsRasterProjector *projector = pipe->projector();
      if ( projector && projector->destCrs() != projector->srcCrs() )
      {
        QgsCoordinateTransform ct( projector->destCrs(), projector->srcCrs() );
        srcExtent = ct.transformBoundingBox( outputExtent );
      }
      if ( !srcProvider->extent().contains( srcExtent ) )
      {
        // Destination extent is larger than source extent, we need destination no data values
        // Get src sample statistics (estimation from sample)
        QgsRasterBandStats stats = srcProvider->bandStatistics( bandNo, QgsRasterBandStats::Min | QgsRasterBandStats::Max, srcExtent, 250000 );

        // Test if we have free (not used) values
        double typeMinValue = QgsContrastEnhancement::maximumValuePossible(( QGis::DataType )srcProvider->srcDataType( bandNo ) );
        double typeMaxValue = QgsContrastEnhancement::maximumValuePossible(( QGis::DataType )srcProvider->srcDataType( bandNo ) );
        if ( stats.minimumValue > typeMinValue )
        {
          destNoDataValue = typeMinValue;
        }
        else if ( stats.maximumValue < typeMaxValue )
        {
          destNoDataValue = typeMaxValue;
        }
        else
        {
          // We have to use wider type
          destDataType = QgsRasterBlock::typeWithNoDataValue( destDataType, &destNoDataValue );
        }
        destHasNoDataValue = true;
      }
    }

    if ( nuller && destHasNoDataValue )
    {
      nuller->setOutputNoDataValue( bandNo, destNoDataValue );
    }

    QgsDebugMsg( QString( "bandNo = %1 destDataType = %2 destHasNoDataValue = %3 destNoDataValue = %4" ).arg( bandNo ).arg( destDataType ).arg( destHasNoDataValue ).arg( destNoDataValue ) );
    destDataTypeList.append( destDataType );
    destHasNoDataValueList.append( destHasNoDataValue );
    destNoDataValueList.append( destNoDataValue );
  }

  QGis::DataType destDataType = destDataTypeList.value( 0 );
  // Currently write API supports one output type for dataset only -> find the widest
  for ( int i = 1; i < nBands; i++ )
  {
    if ( destDataTypeList.value( i ) > destDataType )
    {
      destDataType = destDataTypeList.value( i );
      // no data value may be left per band (for future)
    }
  }

  //create destProvider for whole dataset here
  QgsRasterDataProvider* destProvider = 0;
  double pixelSize;
  double geoTransform[6];
  globalOutputParameters( outputExtent, nCols, nRows, geoTransform, pixelSize );

  // initOutput() returns 0 in tile mode!
  destProvider = initOutput( nCols, nRows, crs, geoTransform, nBands, destDataType, destHasNoDataValueList, destNoDataValueList );

  WriterError error = writeDataRaster( pipe, iter, nCols, nRows, outputExtent, crs, destDataType, destHasNoDataValueList, destNoDataValueList, destProvider, progressDialog );

  if ( error == NoDataConflict )
  {
    // The value used for no data was found in source data, we must use wider data type
    if ( destProvider ) // no tiles
    {
      destProvider->remove();
      delete destProvider;
      destProvider = 0;
    }
    else // VRT
    {
      // TODO: remove created VRT
    }

    // But we don't know which band -> wider all
    for ( int i = 0; i < nBands; i++ )
    {
      double destNoDataValue;
      QGis::DataType destDataType = QgsRasterBlock::typeWithNoDataValue( destDataTypeList.value( i ), &destNoDataValue );
      destDataTypeList.replace( i, destDataType );
      destNoDataValueList.replace( i, destNoDataValue );
    }
    destDataType =  destDataTypeList.value( 0 );

    // Try again
    destProvider = initOutput( nCols, nRows, crs, geoTransform, nBands, destDataType, destHasNoDataValueList, destNoDataValueList );
    error = writeDataRaster( pipe, iter, nCols, nRows, outputExtent, crs, destDataType, destHasNoDataValueList, destNoDataValueList, destProvider, progressDialog );
  }

  if ( destProvider )
    delete destProvider;

  return error;
}