QGIS API Documentation  master-6227475
src/core/raster/qgsrasterprojector.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002     qgsrasterprojector.cpp - Raster projector
00003      --------------------------------------
00004     Date                 : Jan 16, 2011
00005     Copyright            : (C) 2005 by Radim Blazek
00006     email                : radim dot blazek at gmail dot com
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 
00018 #include "qgsrasterdataprovider.h"
00019 #include "qgslogger.h"
00020 #include "qgsrasterprojector.h"
00021 #include "qgscoordinatetransform.h"
00022 
00023 QgsRasterProjector::QgsRasterProjector(
00024   QgsCoordinateReferenceSystem theSrcCRS,
00025   QgsCoordinateReferenceSystem theDestCRS,
00026   QgsRectangle theDestExtent,
00027   int theDestRows, int theDestCols,
00028   double theMaxSrcXRes, double theMaxSrcYRes,
00029   QgsRectangle theExtent )
00030     : QgsRasterInterface( 0 )
00031     , mSrcCRS( theSrcCRS )
00032     , mDestCRS( theDestCRS )
00033     , mCoordinateTransform( theDestCRS, theSrcCRS )
00034     , mDestExtent( theDestExtent )
00035     , mExtent( theExtent )
00036     , mDestRows( theDestRows ), mDestCols( theDestCols )
00037     , pHelperTop( 0 ), pHelperBottom( 0 )
00038     , mMaxSrcXRes( theMaxSrcXRes ), mMaxSrcYRes( theMaxSrcYRes )
00039 {
00040   QgsDebugMsg( "Entered" );
00041   QgsDebugMsg( "theDestExtent = " + theDestExtent.toString() );
00042 
00043   calc();
00044 }
00045 
00046 QgsRasterProjector::QgsRasterProjector(
00047   QgsCoordinateReferenceSystem theSrcCRS,
00048   QgsCoordinateReferenceSystem theDestCRS,
00049   double theMaxSrcXRes, double theMaxSrcYRes,
00050   QgsRectangle theExtent )
00051     : QgsRasterInterface( 0 )
00052     , mSrcCRS( theSrcCRS )
00053     , mDestCRS( theDestCRS )
00054     , mCoordinateTransform( theDestCRS, theSrcCRS )
00055     , mExtent( theExtent )
00056     , pHelperTop( 0 ), pHelperBottom( 0 )
00057     , mMaxSrcXRes( theMaxSrcXRes ), mMaxSrcYRes( theMaxSrcYRes )
00058 {
00059   QgsDebugMsg( "Entered" );
00060 }
00061 
00062 QgsRasterProjector::QgsRasterProjector()
00063     : QgsRasterInterface( 0 )
00064     , pHelperTop( 0 ), pHelperBottom( 0 )
00065 {
00066   QgsDebugMsg( "Entered" );
00067 }
00068 
00069 QgsRasterProjector::QgsRasterProjector( const QgsRasterProjector &projector )
00070     : QgsRasterInterface( 0 )
00071 {
00072   mSrcCRS = projector.mSrcCRS;
00073   mDestCRS = projector.mDestCRS;
00074   mMaxSrcXRes = projector.mMaxSrcXRes;
00075   mMaxSrcYRes = projector.mMaxSrcYRes;
00076   mExtent = projector.mExtent;
00077   mCoordinateTransform.setSourceCrs( mSrcCRS );
00078   mCoordinateTransform.setDestCRS( mDestCRS );
00079 }
00080 
00081 QgsRasterProjector & QgsRasterProjector::operator=( const QgsRasterProjector & projector )
00082 {
00083   if ( &projector != this )
00084   {
00085     mSrcCRS = projector.mSrcCRS;
00086     mDestCRS = projector.mDestCRS;
00087     mMaxSrcXRes = projector.mMaxSrcXRes;
00088     mMaxSrcYRes = projector.mMaxSrcYRes;
00089     mExtent = projector.mExtent;
00090     mCoordinateTransform.setSourceCrs( mSrcCRS );
00091     mCoordinateTransform.setDestCRS( mDestCRS );
00092   }
00093   return *this;
00094 }
00095 
00096 QgsRasterInterface * QgsRasterProjector::clone() const
00097 {
00098   QgsDebugMsg( "Entered" );
00099   QgsRasterProjector * projector = new QgsRasterProjector( mSrcCRS, mDestCRS, mMaxSrcXRes, mMaxSrcYRes, mExtent );
00100   return projector;
00101 }
00102 
00103 QgsRasterProjector::~QgsRasterProjector()
00104 {
00105   delete[] pHelperTop;
00106   delete[] pHelperBottom;
00107 }
00108 
00109 int QgsRasterProjector::bandCount() const
00110 {
00111   if ( mInput ) return mInput->bandCount();
00112 
00113   return 0;
00114 }
00115 
00116 QGis::DataType QgsRasterProjector::dataType( int bandNo ) const
00117 {
00118   if ( mInput ) return mInput->dataType( bandNo );
00119 
00120   return QGis::UnknownDataType;
00121 }
00122 
00123 void QgsRasterProjector::setCRS( const QgsCoordinateReferenceSystem & theSrcCRS, const QgsCoordinateReferenceSystem & theDestCRS )
00124 {
00125   mSrcCRS = theSrcCRS;
00126   mDestCRS = theDestCRS;
00127   mCoordinateTransform.setSourceCrs( theDestCRS );
00128   mCoordinateTransform.setDestCRS( theSrcCRS );
00129 }
00130 
00131 void QgsRasterProjector::calc()
00132 {
00133   QgsDebugMsg( "Entered" );
00134   mCPMatrix.clear();
00135   mCPLegalMatrix.clear();
00136   delete[] pHelperTop;
00137   pHelperTop = 0;
00138   delete[] pHelperBottom;
00139   pHelperBottom = 0;
00140 
00141   // Get max source resolution and extent if possible
00142   mMaxSrcXRes = 0;
00143   mMaxSrcYRes = 0;
00144   if ( mInput )
00145   {
00146     QgsRasterDataProvider *provider = dynamic_cast<QgsRasterDataProvider*>( mInput->srcInput() );
00147     if ( provider && ( provider->capabilities() & QgsRasterDataProvider::Size ) )
00148     {
00149       mMaxSrcXRes = provider->extent().width() / provider->xSize();
00150       mMaxSrcYRes = provider->extent().height() / provider->ySize();
00151     }
00152     // Get source extent
00153     if ( mExtent.isEmpty() )
00154     {
00155       mExtent = provider->extent();
00156     }
00157   }
00158 
00159   mDestXRes = mDestExtent.width() / ( mDestCols );
00160   mDestYRes = mDestExtent.height() / ( mDestRows );
00161 
00162   // Calculate tolerance
00163   // TODO: Think it over better
00164   // Note: we are checking on matrix each even point, that means that the real error
00165   // in that moment is approximately half size
00166   double myDestRes = mDestXRes < mDestYRes ? mDestXRes : mDestYRes;
00167   mSqrTolerance = myDestRes * myDestRes;
00168 
00169   // Initialize the matrix by corners and middle points
00170   mCPCols = mCPRows = 3;
00171   for ( int i = 0; i < mCPRows; i++ )
00172   {
00173     QList<QgsPoint> myRow;
00174     myRow.append( QgsPoint() );
00175     myRow.append( QgsPoint() );
00176     myRow.append( QgsPoint() );
00177     mCPMatrix.insert( i,  myRow );
00178     // And the legal points
00179     QList<bool> myLegalRow;
00180     myLegalRow.append( bool( false ) );
00181     myLegalRow.append( bool( false ) );
00182     myLegalRow.append( bool( false ) );
00183     mCPLegalMatrix.insert( i,  myLegalRow );
00184   }
00185   for ( int i = 0; i < mCPRows; i++ )
00186   {
00187     calcRow( i );
00188   }
00189 
00190   while ( true )
00191   {
00192     bool myColsOK = checkCols();
00193     if ( !myColsOK )
00194     {
00195       insertRows();
00196     }
00197     bool myRowsOK = checkRows();
00198     if ( !myRowsOK )
00199     {
00200       insertCols();
00201     }
00202     if ( myColsOK && myRowsOK )
00203     {
00204       QgsDebugMsg( "CP matrix within tolerance" );
00205       mApproximate = true;
00206       break;
00207     }
00208     // What is the maximum reasonable size of transformatio matrix?
00209     // TODO: consider better when to break - ratio
00210     if ( mCPRows * mCPCols > 0.25 * mDestRows * mDestCols )
00211     {
00212       QgsDebugMsg( "Too large CP matrix" );
00213       mApproximate = false;
00214       break;
00215     }
00216   }
00217   QgsDebugMsg( QString( "CPMatrix size: mCPRows = %1 mCPCols = %2" ).arg( mCPRows ).arg( mCPCols ) );
00218   mDestRowsPerMatrixRow = ( float )mDestRows / ( mCPRows - 1 );
00219   mDestColsPerMatrixCol = ( float )mDestCols / ( mCPCols - 1 );
00220 
00221   QgsDebugMsgLevel( "CPMatrix:", 5 );
00222   QgsDebugMsgLevel( cpToString(), 5 );
00223 
00224   // Calculate source dimensions
00225   calcSrcExtent();
00226   calcSrcRowsCols();
00227   mSrcYRes = mSrcExtent.height() / mSrcRows;
00228   mSrcXRes = mSrcExtent.width() / mSrcCols;
00229 
00230   // init helper points
00231   pHelperTop = new QgsPoint[mDestCols];
00232   pHelperBottom = new QgsPoint[mDestCols];
00233   calcHelper( 0, pHelperTop );
00234   calcHelper( 1, pHelperBottom );
00235   mHelperTopRow = 0;
00236 }
00237 
00238 void QgsRasterProjector::calcSrcExtent()
00239 {
00240   /* Run around the mCPMatrix and find source extent */
00241   // Attention, source limits are not necessarily on destination edges, e.g.
00242   // for destination EPSG:32661 Polar Stereographic and source EPSG:4326,
00243   // the maximum y may be in the middle of destination extent
00244   // TODO: How to find extent exactly and quickly?
00245   // For now, we runt through all matrix
00246   QgsPoint myPoint = mCPMatrix[0][0];
00247   mSrcExtent = QgsRectangle( myPoint.x(), myPoint.y(), myPoint.x(), myPoint.y() );
00248   for ( int i = 0; i < mCPRows; i++ )
00249   {
00250     for ( int j = 0; j < mCPCols ; j++ )
00251     {
00252       myPoint = mCPMatrix[i][j];
00253       if ( mCPLegalMatrix[i][j] )
00254       {
00255         mSrcExtent.combineExtentWith( myPoint.x(), myPoint.y() );
00256       }
00257     }
00258   }
00259   // Expand a bit to avoid possible approx coords falling out because of representation error?
00260 
00261   // If mMaxSrcXRes, mMaxSrcYRes are defined (fixed src resolution)
00262   // align extent to src resolution to avoid jumping of reprojected pixels
00263   // when shifting resampled grid.
00264   // Important especially if we are over mMaxSrcXRes, mMaxSrcYRes limits
00265   // Note however, that preceeding filters (like resampler) may read data
00266   // on different resolution.
00267 
00268   QgsDebugMsg( "mSrcExtent = " + mSrcExtent.toString() );
00269   QgsDebugMsg( "mExtent = " + mExtent.toString() );
00270   if ( !mExtent.isEmpty() )
00271   {
00272     if ( mMaxSrcXRes > 0 )
00273     {
00274       // with floor/ceil it should work correctly also for mSrcExtent.xMinimum() < mExtent.xMinimum()
00275       double col = floor(( mSrcExtent.xMinimum() - mExtent.xMinimum() ) / mMaxSrcXRes );
00276       double x = mExtent.xMinimum() + col * mMaxSrcXRes;
00277       mSrcExtent.setXMinimum( x );
00278 
00279       col = ceil(( mSrcExtent.xMaximum() - mExtent.xMinimum() ) / mMaxSrcXRes );
00280       x = mExtent.xMinimum() + col * mMaxSrcXRes;
00281       mSrcExtent.setXMaximum( x );
00282     }
00283     if ( mMaxSrcYRes > 0 )
00284     {
00285       double row = floor(( mExtent.yMaximum() - mSrcExtent.yMaximum() ) / mMaxSrcYRes );
00286       double y = mExtent.yMaximum() - row * mMaxSrcYRes;
00287       mSrcExtent.setYMaximum( y );
00288 
00289       row = ceil(( mExtent.yMaximum() - mSrcExtent.yMinimum() ) / mMaxSrcYRes );
00290       y = mExtent.yMaximum() - row * mMaxSrcYRes;
00291       mSrcExtent.setYMinimum( y );
00292     }
00293   }
00294   QgsDebugMsg( "mSrcExtent = " + mSrcExtent.toString() );
00295 }
00296 
00297 QString QgsRasterProjector::cpToString()
00298 {
00299   QString myString;
00300   for ( int i = 0; i < mCPRows; i++ )
00301   {
00302     if ( i > 0 )
00303       myString += "\n";
00304     for ( int j = 0; j < mCPCols; j++ )
00305     {
00306       if ( j > 0 )
00307         myString += "  ";
00308       QgsPoint myPoint = mCPMatrix[i][j];
00309       if ( mCPLegalMatrix[i][j] )
00310       {
00311         myString += myPoint.toString();
00312       }
00313       else
00314       {
00315         myString += "(-,-)";
00316       }
00317     }
00318   }
00319   return myString;
00320 }
00321 
00322 void QgsRasterProjector::calcSrcRowsCols()
00323 {
00324   // Wee need to calculate minimum cell size in the source
00325   // TODO: Think it over better, what is the right source resolution?
00326   //       Taking distances between cell centers projected to source along source
00327   //       axis would result in very high resolution
00328   // TODO: different resolution for rows and cols ?
00329 
00330   // For now, we take cell sizes projected to source but not to source axes
00331   double myDestColsPerMatrixCell = mDestCols / mCPCols;
00332   double myDestRowsPerMatrixCell = mDestRows / mCPRows;
00333   QgsDebugMsg( QString( "myDestColsPerMatrixCell = %1 myDestRowsPerMatrixCell = %2" ).arg( myDestColsPerMatrixCell ).arg( myDestRowsPerMatrixCell ) );
00334 
00335   double myMinSize = DBL_MAX;
00336 
00337   for ( int i = 0; i < mCPRows - 1; i++ )
00338   {
00339     for ( int j = 0; j < mCPCols - 1; j++ )
00340     {
00341       QgsPoint myPointA = mCPMatrix[i][j];
00342       QgsPoint myPointB = mCPMatrix[i][j+1];
00343       QgsPoint myPointC = mCPMatrix[i+1][j];
00344       if ( mCPLegalMatrix[i][j] && mCPLegalMatrix[i][j+1] && mCPLegalMatrix[i+1][j] )
00345       {
00346         double mySize = sqrt( myPointA.sqrDist( myPointB ) ) / myDestColsPerMatrixCell;
00347         if ( mySize < myMinSize )
00348           myMinSize = mySize;
00349 
00350         mySize = sqrt( myPointA.sqrDist( myPointC ) ) / myDestRowsPerMatrixCell;
00351         if ( mySize < myMinSize )
00352           myMinSize = mySize;
00353       }
00354     }
00355   }
00356 
00357   // Make it a bit higher resolution
00358   // TODO: find the best coefficient, attention, increasing resolution for WMS
00359   // is changing WMS content
00360   myMinSize *= 0.75;
00361 
00362   QgsDebugMsg( QString( "mMaxSrcXRes = %1 mMaxSrcYRes = %2" ).arg( mMaxSrcXRes ).arg( mMaxSrcYRes ) );
00363   // mMaxSrcXRes, mMaxSrcYRes may be 0 - no limit (WMS)
00364   double myMinXSize = mMaxSrcXRes > myMinSize ? mMaxSrcXRes : myMinSize;
00365   double myMinYSize = mMaxSrcYRes > myMinSize ? mMaxSrcYRes : myMinSize;
00366   QgsDebugMsg( QString( "myMinXSize = %1 myMinYSize = %2" ).arg( myMinXSize ).arg( myMinYSize ) );
00367   QgsDebugMsg( QString( "mSrcExtent.width = %1 mSrcExtent.height = %2" ).arg( mSrcExtent.width() ).arg( mSrcExtent.height() ) );
00368 
00369   // we have to round to keep alignment set in calcSrcExtent
00370   mSrcRows = ( int ) qRound( mSrcExtent.height() / myMinYSize );
00371   mSrcCols = ( int ) qRound( mSrcExtent.width() / myMinXSize );
00372 
00373   QgsDebugMsg( QString( "mSrcRows = %1 mSrcCols = %2" ).arg( mSrcRows ).arg( mSrcCols ) );
00374 }
00375 
00376 
00377 inline void QgsRasterProjector::destPointOnCPMatrix( int theRow, int theCol, double *theX, double *theY )
00378 {
00379   *theX = mDestExtent.xMinimum() + theCol * mDestExtent.width() / ( mCPCols - 1 );
00380   *theY = mDestExtent.yMaximum() - theRow * mDestExtent.height() / ( mCPRows - 1 );
00381 }
00382 
00383 inline int QgsRasterProjector::matrixRow( int theDestRow )
00384 {
00385   return ( int )( floor(( theDestRow + 0.5 ) / mDestRowsPerMatrixRow ) );
00386 }
00387 inline int QgsRasterProjector::matrixCol( int theDestCol )
00388 {
00389   return ( int )( floor(( theDestCol + 0.5 ) / mDestColsPerMatrixCol ) );
00390 }
00391 
00392 QgsPoint QgsRasterProjector::srcPoint( int theDestRow, int theCol )
00393 {
00394   Q_UNUSED( theDestRow );
00395   Q_UNUSED( theCol );
00396   return QgsPoint();
00397 }
00398 
00399 void QgsRasterProjector::calcHelper( int theMatrixRow, QgsPoint *thePoints )
00400 {
00401   // TODO?: should we also precalc dest cell center coordinates for x and y?
00402   for ( int myDestCol = 0; myDestCol < mDestCols; myDestCol++ )
00403   {
00404     double myDestX = mDestExtent.xMinimum() + ( myDestCol + 0.5 ) * mDestXRes;
00405 
00406     int myMatrixCol = matrixCol( myDestCol );
00407 
00408     double myDestXMin, myDestYMin, myDestXMax, myDestYMax;
00409 
00410     destPointOnCPMatrix( theMatrixRow, myMatrixCol, &myDestXMin, &myDestYMin );
00411     destPointOnCPMatrix( theMatrixRow, myMatrixCol + 1, &myDestXMax, &myDestYMax );
00412 
00413     double xfrac = ( myDestX - myDestXMin ) / ( myDestXMax - myDestXMin );
00414 
00415     QgsPoint &mySrcPoint0 = mCPMatrix[theMatrixRow][myMatrixCol];
00416     QgsPoint &mySrcPoint1 = mCPMatrix[theMatrixRow][myMatrixCol+1];
00417     double s = mySrcPoint0.x() + ( mySrcPoint1.x() - mySrcPoint0.x() ) * xfrac;
00418     double t = mySrcPoint0.y() + ( mySrcPoint1.y() - mySrcPoint0.y() ) * xfrac;
00419 
00420     thePoints[myDestCol].setX( s );
00421     thePoints[myDestCol].setY( t );
00422   }
00423 }
00424 void QgsRasterProjector::nextHelper()
00425 {
00426   // We just switch pHelperTop and pHelperBottom, memory is not lost
00427   QgsPoint *tmp;
00428   tmp = pHelperTop;
00429   pHelperTop = pHelperBottom;
00430   pHelperBottom = tmp;
00431   calcHelper( mHelperTopRow + 2, pHelperBottom );
00432   mHelperTopRow++;
00433 }
00434 
00435 void QgsRasterProjector::srcRowCol( int theDestRow, int theDestCol, int *theSrcRow, int *theSrcCol )
00436 {
00437   if ( mApproximate )
00438   {
00439     approximateSrcRowCol( theDestRow, theDestCol, theSrcRow, theSrcCol );
00440   }
00441   else
00442   {
00443     preciseSrcRowCol( theDestRow, theDestCol, theSrcRow, theSrcCol );
00444   }
00445 }
00446 
00447 void QgsRasterProjector::preciseSrcRowCol( int theDestRow, int theDestCol, int *theSrcRow, int *theSrcCol )
00448 {
00449 #ifdef QGISDEBUG
00450   QgsDebugMsgLevel( QString( "theDestRow = %1" ).arg( theDestRow ), 5 );
00451   QgsDebugMsgLevel( QString( "theDestRow = %1 mDestExtent.yMaximum() = %2 mDestYRes = %3" ).arg( theDestRow ).arg( mDestExtent.yMaximum() ).arg( mDestYRes ), 5 );
00452 #endif
00453 
00454   // Get coordinate of center of destination cell
00455   double x = mDestExtent.xMinimum() + ( theDestCol + 0.5 ) * mDestXRes;
00456   double y = mDestExtent.yMaximum() - ( theDestRow + 0.5 ) * mDestYRes;
00457   double z = 0;
00458 
00459 #ifdef QGISDEBUG
00460   QgsDebugMsgLevel( QString( "x = %1 y = %2" ).arg( x ).arg( y ), 5 );
00461 #endif
00462 
00463   mCoordinateTransform.transformInPlace( x, y, z );
00464 
00465 #ifdef QGISDEBUG
00466   QgsDebugMsgLevel( QString( "x = %1 y = %2" ).arg( x ).arg( y ), 5 );
00467 #endif
00468 
00469   // Get source row col
00470   *theSrcRow = ( int ) floor(( mSrcExtent.yMaximum() - y ) / mSrcYRes );
00471   *theSrcCol = ( int ) floor(( x - mSrcExtent.xMinimum() ) / mSrcXRes );
00472 #ifdef QGISDEBUG
00473   QgsDebugMsgLevel( QString( "mSrcExtent.yMaximum() = %1 mSrcYRes = %2" ).arg( mSrcExtent.yMaximum() ).arg( mSrcYRes ), 5 );
00474   QgsDebugMsgLevel( QString( "theSrcRow = %1 theSrcCol = %2" ).arg( *theSrcRow ).arg( *theSrcCol ), 5 );
00475 #endif
00476 
00477   // With epsg 32661 (Polar Stereographic) it was happening that *theSrcCol == mSrcCols
00478   // For now silently correct limits to avoid crashes
00479   // TODO: review
00480   if ( *theSrcRow >= mSrcRows )
00481     *theSrcRow = mSrcRows - 1;
00482   if ( *theSrcRow < 0 )
00483     *theSrcRow = 0;
00484   if ( *theSrcCol >= mSrcCols )
00485     *theSrcCol = mSrcCols - 1;
00486   if ( *theSrcCol < 0 )
00487     *theSrcCol = 0;
00488 
00489   Q_ASSERT( *theSrcRow < mSrcRows );
00490   Q_ASSERT( *theSrcCol < mSrcCols );
00491 }
00492 
00493 void QgsRasterProjector::approximateSrcRowCol( int theDestRow, int theDestCol, int *theSrcRow, int *theSrcCol )
00494 {
00495   int myMatrixRow = matrixRow( theDestRow );
00496   int myMatrixCol = matrixCol( theDestCol );
00497 
00498   if ( myMatrixRow > mHelperTopRow )
00499   {
00500     // TODO: make it more robust (for random, not sequential reading)
00501     nextHelper();
00502   }
00503 
00504   double myDestY = mDestExtent.yMaximum() - ( theDestRow + 0.5 ) * mDestYRes;
00505 
00506   // See the schema in javax.media.jai.WarpGrid doc (but up side down)
00507   // TODO: use some kind of cache of values which can be reused
00508   double myDestXMin, myDestYMin, myDestXMax, myDestYMax;
00509 
00510   destPointOnCPMatrix( myMatrixRow + 1, myMatrixCol, &myDestXMin, &myDestYMin );
00511   destPointOnCPMatrix( myMatrixRow, myMatrixCol + 1, &myDestXMax, &myDestYMax );
00512 
00513   double yfrac = ( myDestY - myDestYMin ) / ( myDestYMax - myDestYMin );
00514 
00515   QgsPoint &myTop = pHelperTop[theDestCol];
00516   QgsPoint &myBot = pHelperBottom[theDestCol];
00517 
00518   // Warning: this is very SLOW compared to the following code!:
00519   //double mySrcX = myBot.x() + (myTop.x() - myBot.x()) * yfrac;
00520   //double mySrcY = myBot.y() + (myTop.y() - myBot.y()) * yfrac;
00521 
00522   double tx = myTop.x();
00523   double ty = myTop.y();
00524   double bx = myBot.x();
00525   double by = myBot.y();
00526   double mySrcX = bx + ( tx - bx ) * yfrac;
00527   double mySrcY = by + ( ty - by ) * yfrac;
00528 
00529   // TODO: check again cell selection (coor is in the middle)
00530 
00531   *theSrcRow = ( int ) floor(( mSrcExtent.yMaximum() - mySrcY ) / mSrcYRes );
00532   *theSrcCol = ( int ) floor(( mySrcX - mSrcExtent.xMinimum() ) / mSrcXRes );
00533 
00534   // For now silently correct limits to avoid crashes
00535   // TODO: review
00536   if ( *theSrcRow >= mSrcRows )
00537     *theSrcRow = mSrcRows - 1;
00538   if ( *theSrcRow < 0 )
00539     *theSrcRow = 0;
00540   if ( *theSrcCol >= mSrcCols )
00541     *theSrcCol = mSrcCols - 1;
00542   if ( *theSrcCol < 0 )
00543     *theSrcCol = 0;
00544   Q_ASSERT( *theSrcRow < mSrcRows );
00545   Q_ASSERT( *theSrcCol < mSrcCols );
00546 }
00547 
00548 void QgsRasterProjector::insertRows()
00549 {
00550   for ( int r = 0; r < mCPRows - 1; r++ )
00551   {
00552     QList<QgsPoint> myRow;
00553     QList<bool> myLegalRow;
00554     for ( int c = 0; c < mCPCols; c++ )
00555     {
00556       myRow.append( QgsPoint() );
00557       myLegalRow.append( false );
00558     }
00559     QgsDebugMsgLevel( QString( "insert new row at %1" ).arg( 1 + r*2 ), 3 );
00560     mCPMatrix.insert( 1 + r*2,  myRow );
00561     mCPLegalMatrix.insert( 1 + r*2,  myLegalRow );
00562   }
00563   mCPRows += mCPRows - 1;
00564   for ( int r = 1; r < mCPRows - 1; r += 2 )
00565   {
00566     calcRow( r );
00567   }
00568 }
00569 
00570 void QgsRasterProjector::insertCols()
00571 {
00572   for ( int r = 0; r < mCPRows; r++ )
00573   {
00574     QList<QgsPoint> myRow;
00575     QList<bool> myLegalRow;
00576     for ( int c = 0; c < mCPCols - 1; c++ )
00577     {
00578       mCPMatrix[r].insert( 1 + c*2,  QgsPoint() );
00579       mCPLegalMatrix[r].insert( 1 + c*2,  false );
00580     }
00581   }
00582   mCPCols += mCPCols - 1;
00583   for ( int c = 1; c < mCPCols - 1; c += 2 )
00584   {
00585     calcCol( c );
00586   }
00587 
00588 }
00589 
00590 void QgsRasterProjector::calcCP( int theRow, int theCol )
00591 {
00592   double myDestX, myDestY;
00593   destPointOnCPMatrix( theRow, theCol, &myDestX, &myDestY );
00594   QgsPoint myDestPoint( myDestX, myDestY );
00595   try
00596   {
00597     mCPMatrix[theRow][theCol] = mCoordinateTransform.transform( myDestPoint );
00598     mCPLegalMatrix[theRow][theCol] = true;
00599   }
00600   catch ( QgsCsException &e )
00601   {
00602     Q_UNUSED( e );
00603     // Caught an error in transform
00604     mCPLegalMatrix[theRow][theCol] = false;
00605   }
00606 }
00607 
00608 bool QgsRasterProjector::calcRow( int theRow )
00609 {
00610   QgsDebugMsgLevel( QString( "theRow = %1" ).arg( theRow ), 3 );
00611   for ( int i = 0; i < mCPCols; i++ )
00612   {
00613     calcCP( theRow, i );
00614   }
00615 
00616   return true;
00617 }
00618 
00619 bool QgsRasterProjector::calcCol( int theCol )
00620 {
00621   QgsDebugMsgLevel( QString( "theCol = %1" ).arg( theCol ), 3 );
00622   for ( int i = 0; i < mCPRows; i++ )
00623   {
00624     calcCP( i, theCol );
00625   }
00626 
00627   return true;
00628 }
00629 
00630 bool QgsRasterProjector::checkCols()
00631 {
00632   for ( int c = 0; c < mCPCols; c++ )
00633   {
00634     for ( int r = 1; r < mCPRows - 1; r += 2 )
00635     {
00636       double myDestX, myDestY;
00637       destPointOnCPMatrix( r, c, &myDestX, &myDestY );
00638       QgsPoint myDestPoint( myDestX, myDestY );
00639 
00640       QgsPoint mySrcPoint1 = mCPMatrix[r-1][c];
00641       QgsPoint mySrcPoint2 = mCPMatrix[r][c];
00642       QgsPoint mySrcPoint3 = mCPMatrix[r+1][c];
00643 
00644       QgsPoint mySrcApprox(( mySrcPoint1.x() + mySrcPoint3.x() ) / 2, ( mySrcPoint1.y() + mySrcPoint3.y() ) / 2 );
00645       if ( !mCPLegalMatrix[r-1][c] || !mCPLegalMatrix[r][c] || !mCPLegalMatrix[r+1][c] )
00646       {
00647         // There was an error earlier in transform, just abort
00648         return false;
00649       }
00650       try
00651       {
00652         QgsPoint myDestApprox = mCoordinateTransform.transform( mySrcApprox, QgsCoordinateTransform::ReverseTransform );
00653         double mySqrDist = myDestApprox.sqrDist( myDestPoint );
00654         if ( mySqrDist > mSqrTolerance )
00655         {
00656           return false;
00657         }
00658       }
00659       catch ( QgsCsException &e )
00660       {
00661         Q_UNUSED( e );
00662         // Caught an error in transform
00663         return false;
00664       }
00665     }
00666   }
00667   return true;
00668 }
00669 
00670 bool QgsRasterProjector::checkRows()
00671 {
00672   for ( int r = 0; r < mCPRows; r++ )
00673   {
00674     for ( int c = 1; c < mCPCols - 1; c += 2 )
00675     {
00676       double myDestX, myDestY;
00677       destPointOnCPMatrix( r, c, &myDestX, &myDestY );
00678 
00679       QgsPoint myDestPoint( myDestX, myDestY );
00680       QgsPoint mySrcPoint1 = mCPMatrix[r][c-1];
00681       QgsPoint mySrcPoint2 = mCPMatrix[r][c];
00682       QgsPoint mySrcPoint3 = mCPMatrix[r][c+1];
00683 
00684       QgsPoint mySrcApprox(( mySrcPoint1.x() + mySrcPoint3.x() ) / 2, ( mySrcPoint1.y() + mySrcPoint3.y() ) / 2 );
00685       if ( !mCPLegalMatrix[r][c-1] || !mCPLegalMatrix[r][c] || !mCPLegalMatrix[r][c+1] )
00686       {
00687         // There was an error earlier in transform, just abort
00688         return false;
00689       }
00690       try
00691       {
00692         QgsPoint myDestApprox = mCoordinateTransform.transform( mySrcApprox, QgsCoordinateTransform::ReverseTransform );
00693         double mySqrDist = myDestApprox.sqrDist( myDestPoint );
00694         if ( mySqrDist > mSqrTolerance )
00695         {
00696           return false;
00697         }
00698       }
00699       catch ( QgsCsException &e )
00700       {
00701         Q_UNUSED( e );
00702         // Caught an error in transform
00703         return false;
00704       }
00705     }
00706   }
00707   return true;
00708 }
00709 
00710 QgsRasterBlock * QgsRasterProjector::block( int bandNo, QgsRectangle  const & extent, int width, int height )
00711 {
00712   QgsDebugMsg( QString( "extent:\n%1" ).arg( extent.toString() ) );
00713   QgsDebugMsg( QString( "width = %1 height = %2" ).arg( width ).arg( height ) );
00714   if ( !mInput )
00715   {
00716     QgsDebugMsg( "Input not set" );
00717     return new QgsRasterBlock();
00718   }
00719 
00720   if ( ! mSrcCRS.isValid() || ! mDestCRS.isValid() || mSrcCRS == mDestCRS )
00721   {
00722     QgsDebugMsg( "No projection necessary" );
00723     return mInput->block( bandNo, extent, width, height );
00724   }
00725 
00726   mDestExtent = extent;
00727   mDestRows = height;
00728   mDestCols = width;
00729   calc();
00730 
00731   QgsDebugMsg( QString( "srcExtent:\n%1" ).arg( srcExtent().toString() ) );
00732   QgsDebugMsg( QString( "srcCols = %1 srcRows = %2" ).arg( srcCols() ).arg( srcRows() ) );
00733 
00734   // If we zoom out too much, projector srcRows / srcCols maybe 0, which can cause problems in providers
00735   if ( srcRows() <= 0 || srcCols() <= 0 )
00736   {
00737     QgsDebugMsg( "Zero srcRows or srcCols" );
00738     return new QgsRasterBlock();
00739   }
00740 
00741   QgsRasterBlock *inputBlock = mInput->block( bandNo, srcExtent(), srcCols(), srcRows() );
00742   if ( !inputBlock || inputBlock->isEmpty() )
00743   {
00744     QgsDebugMsg( "No raster data!" );
00745     delete inputBlock;
00746     return new QgsRasterBlock();
00747   }
00748 
00749   size_t pixelSize = QgsRasterBlock::typeSize( mInput->dataType( bandNo ) );
00750 
00751   QgsRasterBlock *outputBlock;
00752   if ( inputBlock->hasNoDataValue() )
00753   {
00754     outputBlock = new QgsRasterBlock( inputBlock->dataType(), width, height, inputBlock->noDataValue() );
00755   }
00756   else
00757   {
00758     outputBlock = new QgsRasterBlock( inputBlock->dataType(), width, height );
00759   }
00760   if ( !outputBlock->isValid() )
00761   {
00762     QgsDebugMsg( "Cannot create block" );
00763     delete inputBlock;
00764     return outputBlock;
00765   }
00766 
00767   // No data:
00768   // 1) no data value exists (numerical) -> memcpy, not necessary isNoData()/setIsNoData()
00769   // 2) no data value does not exist but it may contain no data (numerical no data bitmap)
00770   //    -> must use isNoData()/setIsNoData()
00771   // 3) no data are not used (no no data value, no no data bitmap) -> simple memcpy
00772   // 4) image - simple memcpy
00773 
00774   // To copy no data values stored in bitmaps we have to use isNoData()/setIsNoData(),
00775   // we cannot fill output block with no data because we use memcpy for data, not setValue().
00776   bool doNoData = inputBlock->hasNoData() && !inputBlock->hasNoDataValue();
00777 
00778   int srcRow, srcCol;
00779   for ( int i = 0; i < height; ++i )
00780   {
00781     for ( int j = 0; j < width; ++j )
00782     {
00783       srcRowCol( i, j, &srcRow, &srcCol );
00784       size_t srcIndex = ( size_t )srcRow * mSrcCols + srcCol;
00785       QgsDebugMsgLevel( QString( "row = %1 col = %2 srcRow = %3 srcCol = %4" ).arg( i ).arg( j ).arg( srcRow ).arg( srcCol ), 5 );
00786 
00787       // isNoData() may be slow so we check doNoData first
00788       if ( doNoData && inputBlock->isNoData( srcRow, srcCol ) )
00789       {
00790         outputBlock->setIsNoData( srcRow, srcCol );
00791         continue ;
00792       }
00793 
00794       size_t destIndex = ( size_t )i * width + j;
00795       char *srcBits = inputBlock->bits( srcIndex );
00796       char *destBits = outputBlock->bits( destIndex );
00797       if ( !srcBits )
00798       {
00799         QgsDebugMsg( QString( "Cannot get input block data: row = %1 col = %2" ).arg( i ).arg( j ) );
00800         continue;
00801       }
00802       if ( !destBits )
00803       {
00804         QgsDebugMsg( QString( "Cannot set output block data: srcRow = %1 srcCol = %2" ).arg( srcRow ).arg( srcCol ) );
00805         continue;
00806       }
00807       memcpy( destBits, srcBits, pixelSize );
00808     }
00809   }
00810 
00811   delete inputBlock;
00812 
00813   return outputBlock;
00814 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines