Quantum GIS API Documentation  1.7.4
src/core/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 /* $Id: qgsrasterprojector.cpp 15005 2011-01-08 16:35:21Z rblazek $ */
00018 
00019 #include <cassert>
00020 
00021 #include "qgslogger.h"
00022 #include "qgsrasterprojector.h"
00023 #include "qgscoordinatetransform.h"
00024 
00025 QgsRasterProjector::QgsRasterProjector(
00026   QgsCoordinateReferenceSystem theSrcCRS,
00027   QgsCoordinateReferenceSystem theDestCRS,
00028   QgsRectangle theDestExtent,
00029   int theDestRows, int theDestCols,
00030   double theMaxSrcXRes, double theMaxSrcYRes,
00031   QgsRectangle theExtent )
00032     : mSrcCRS( theSrcCRS )
00033     , mDestCRS( theDestCRS )
00034     , mCoordinateTransform( theDestCRS, theSrcCRS )
00035     , mDestExtent( theDestExtent )
00036     , mExtent( theExtent )
00037     , mDestRows( theDestRows ), mDestCols( theDestCols )
00038     , mMaxSrcXRes( theMaxSrcXRes ), mMaxSrcYRes( theMaxSrcYRes )
00039 {
00040   QgsDebugMsg( "Entered" );
00041   QgsDebugMsg( "theDestExtent = " + theDestExtent.toString() );
00042 
00043   mDestXRes = mDestExtent.width() / ( mDestCols );
00044   mDestYRes = mDestExtent.height() / ( mDestRows );
00045 
00046   // Calculate tolerance
00047   // TODO: Think it over better
00048   // Note: we are checking on matrix each even point, that means taht the real error
00049   // in that moment is approximately half size
00050   double myDestRes = mDestXRes < mDestYRes ? mDestXRes : mDestYRes;
00051   mSqrTolerance = myDestRes * myDestRes;
00052 
00053   // Initialize the matrix by corners and middle points
00054   mCPCols = mCPRows = 3;
00055   for ( int i = 0; i < mCPRows; i++ )
00056   {
00057     QList<QgsPoint> myRow;
00058     myRow.append( QgsPoint() );
00059     myRow.append( QgsPoint() );
00060     myRow.append( QgsPoint() );
00061     mCPMatrix.insert( i,  myRow );
00062   }
00063   for ( int i = 0; i < mCPRows; i++ )
00064   {
00065     calcRow( i );
00066   }
00067 
00068   while ( true )
00069   {
00070     bool myColsOK = checkCols();
00071     if ( !myColsOK )
00072     {
00073       insertRows();
00074     }
00075     bool myRowsOK = checkRows();
00076     if ( !myRowsOK )
00077     {
00078       insertCols();
00079     }
00080     if ( myColsOK && myRowsOK )
00081     {
00082       QgsDebugMsg( "CP matrix within tolerance" );
00083       mApproximate = true;
00084       break;
00085     }
00086     // What is the maximum reasonable size of transformatio matrix?
00087     // TODO: consider better when to break - ratio
00088     if ( mCPRows * mCPCols > 0.0625 * mDestRows * mDestCols )
00089     {
00090       QgsDebugMsg( "Too large CP matrix" );
00091       mApproximate = false;
00092       break;
00093     }
00094 
00095   }
00096   QgsDebugMsg( QString( "CPMatrix size: mCPRows = %1 mCPCols = %2" ).arg( mCPRows ).arg( mCPCols ) );
00097   mDestRowsPerMatrixRow = ( float )mDestRows / ( mCPRows - 1 );
00098   mDestColsPerMatrixCol = ( float )mDestCols / ( mCPCols - 1 );
00099 
00100   //QgsDebugMsg( "CPMatrix:\n" + cpToString() );
00101 
00102   // Calculate source dimensions
00103   calcSrcExtent();
00104   calcSrcRowsCols();
00105   mSrcYRes = mSrcExtent.height() / mSrcRows;
00106   mSrcXRes = mSrcExtent.width() / mSrcCols;
00107 
00108   // init helper points
00109   pHelperTop = new QgsPoint[mDestCols];
00110   pHelperBottom = new QgsPoint[mDestCols];
00111   calcHelper( 0, pHelperTop );
00112   calcHelper( 1, pHelperBottom );
00113   mHelperTopRow = 0;
00114 }
00115 
00116 QgsRasterProjector::~QgsRasterProjector()
00117 {
00118   //delete mCoordinateTransform;
00119 }
00120 
00121 void QgsRasterProjector::calcSrcExtent()
00122 {
00123   /* Run around the mCPMatrix and find source extent */
00124   // Attention, source limits are not necessarily on destination edges, e.g.
00125   // for destination EPSG:32661 Polar Stereographic and source EPSG:4326,
00126   // the maximum y may be in the middle of destination extent
00127   // TODO: How to find extent exactly and quickly?
00128   // For now, we runt through all matrix
00129   QgsPoint myPoint = mCPMatrix[0][0];
00130   mSrcExtent = QgsRectangle( myPoint.x(), myPoint.y(), myPoint.x(), myPoint.y() );
00131   for ( int i = 0; i < mCPRows; i++ )
00132   {
00133     for ( int j = 0; j < mCPCols ; j++ )
00134     {
00135       myPoint = mCPMatrix[i][j];
00136       mSrcExtent.combineExtentWith( myPoint.x(), myPoint.y() );
00137     }
00138   }
00139   // Expand a bit to avoid possible approx coords falling out because of representation error?
00140 
00141   // If mMaxSrcXRes, mMaxSrcYRes are defined (fixed src resolution)
00142   // align extent to src resolution to avoid jumping reprojected pixels
00143   // because of shifting resampled grid
00144   // Important especially if we are over mMaxSrcXRes, mMaxSrcYRes limits
00145 
00146   QgsDebugMsg( "mSrcExtent = " + mSrcExtent.toString() );
00147 
00148   if ( mMaxSrcXRes > 0 )
00149   {
00150     // with floor/ceil it should work correctly also for mSrcExtent.xMinimum() < mExtent.xMinimum()
00151     double col = floor(( mSrcExtent.xMinimum() - mExtent.xMinimum() ) / mMaxSrcXRes );
00152     double x = mExtent.xMinimum() + col * mMaxSrcXRes;
00153     mSrcExtent.setXMinimum( x );
00154 
00155     col = ceil(( mSrcExtent.xMaximum() - mExtent.xMinimum() ) / mMaxSrcXRes );
00156     x = mExtent.xMinimum() + col * mMaxSrcXRes;
00157     mSrcExtent.setXMaximum( x );
00158   }
00159   if ( mMaxSrcYRes > 0 )
00160   {
00161     double row = floor(( mExtent.yMaximum() - mSrcExtent.yMaximum() ) / mMaxSrcYRes );
00162     double y = mExtent.yMaximum() - row * mMaxSrcYRes;
00163     mSrcExtent.setYMaximum( y );
00164 
00165     row = ceil(( mExtent.yMaximum() - mSrcExtent.yMinimum() ) / mMaxSrcYRes );
00166     y = mExtent.yMaximum() - row * mMaxSrcYRes;
00167     mSrcExtent.setYMinimum( y );
00168   }
00169 
00170 
00171   QgsDebugMsg( "mSrcExtent = " + mSrcExtent.toString() );
00172 }
00173 
00174 QString QgsRasterProjector::cpToString()
00175 {
00176   QString myString;
00177   for ( int i = 0; i < mCPRows; i++ )
00178   {
00179     if ( i > 0 ) myString += "\n";
00180     for ( int j = 0; j < mCPCols; j++ )
00181     {
00182       if ( j > 0 ) myString += "  ";
00183       QgsPoint myPoint = mCPMatrix[i][j];
00184       myString += myPoint.toString();
00185     }
00186   }
00187   return myString;
00188 }
00189 
00190 void QgsRasterProjector::calcSrcRowsCols()
00191 {
00192   // Wee need to calculate minimum cell size in the source
00193   // TODO: Think it over better, what is the right source resolution?
00194   //       Taking distances between cell centers projected to source along source
00195   //       axis would result in very high resolution
00196   // TODO: different resolution for rows and cols ?
00197 
00198   // For now, we take cell sizes projected to source but not to source axes
00199   double myDestColsPerMatrixCell = mDestCols / mCPCols;
00200   double myDestRowsPerMatrixCell = mDestRows / mCPRows;
00201   QgsDebugMsg( QString( "myDestColsPerMatrixCell = %1 myDestRowsPerMatrixCell = %2" ).arg( myDestColsPerMatrixCell ).arg( myDestRowsPerMatrixCell ) );
00202 
00203   double myMinSize = DBL_MAX;
00204 
00205   for ( int i = 0; i < mCPRows - 1; i++ )
00206   {
00207     for ( int j = 0; j < mCPCols - 1; j++ )
00208     {
00209       QgsPoint myPointA = mCPMatrix[i][j];
00210       QgsPoint myPointB = mCPMatrix[i][j+1];
00211       QgsPoint myPointC = mCPMatrix[i+1][j];
00212       double mySize = sqrt( myPointA.sqrDist( myPointB ) ) / myDestColsPerMatrixCell;
00213       if ( mySize < myMinSize ) { myMinSize = mySize; }
00214 
00215       mySize = sqrt( myPointA.sqrDist( myPointC ) ) / myDestRowsPerMatrixCell;
00216       if ( mySize < myMinSize ) { myMinSize = mySize; }
00217     }
00218   }
00219 
00220   // Make it a bit higher resolution
00221   // TODO: find the best coefficient, attention, increasing resolution for WMS
00222   // is changing WMS content
00223   myMinSize *= 0.75;
00224 
00225   QgsDebugMsg( QString( "mMaxSrcXRes = %1 mMaxSrcYRes = %2" ).arg( mMaxSrcXRes ).arg( mMaxSrcYRes ) );
00226   // mMaxSrcXRes, mMaxSrcYRes may be 0 - no limit (WMS)
00227   double myMinXSize = mMaxSrcXRes > myMinSize ? mMaxSrcXRes : myMinSize;
00228   double myMinYSize = mMaxSrcYRes > myMinSize ? mMaxSrcYRes : myMinSize;
00229   QgsDebugMsg( QString( "myMinXSize = %1 myMinYSize = %2" ).arg( myMinXSize ).arg( myMinYSize ) );
00230   QgsDebugMsg( QString( "mSrcExtent.width = %1 mSrcExtent.height = %2" ).arg( mSrcExtent.width() ).arg( mSrcExtent.height() ) );
00231 
00232   // we have to round to keep alignment set in calcSrcExtent
00233   mSrcRows = ( int ) qRound( mSrcExtent.height() / myMinYSize );
00234   mSrcCols = ( int ) qRound( mSrcExtent.width() / myMinXSize );
00235 
00236   QgsDebugMsg( QString( "mSrcRows = %1 mSrcCols = %2" ).arg( mSrcRows ).arg( mSrcCols ) );
00237 }
00238 
00239 
00240 inline void QgsRasterProjector::destPointOnCPMatrix( int theRow, int theCol, double *theX, double *theY )
00241 {
00242   *theX = mDestExtent.xMinimum() + theCol * mDestExtent.width() / ( mCPCols - 1 );
00243   *theY = mDestExtent.yMaximum() - theRow * mDestExtent.height() / ( mCPRows - 1 );
00244 }
00245 
00246 inline int QgsRasterProjector::matrixRow( int theDestRow )
00247 {
00248   return ( int )( floor(( theDestRow + 0.5 ) / mDestRowsPerMatrixRow ) );
00249 }
00250 inline int QgsRasterProjector::matrixCol( int theDestCol )
00251 {
00252   return ( int )( floor(( theDestCol + 0.5 ) / mDestColsPerMatrixCol ) );
00253 }
00254 
00255 QgsPoint QgsRasterProjector::srcPoint( int theDestRow, int theCol )
00256 {
00257   return QgsPoint();
00258 }
00259 
00260 void QgsRasterProjector::calcHelper( int theMatrixRow, QgsPoint *thePoints )
00261 {
00262   // TODO?: should we also precalc dest cell center coordinates for x and y?
00263   for ( int myDestCol = 0; myDestCol < mDestCols; myDestCol++ )
00264   {
00265     double myDestX = mDestExtent.xMinimum() + ( myDestCol + 0.5 ) * mDestXRes;
00266 
00267     int myMatrixCol = matrixCol( myDestCol );
00268 
00269     double myDestXMin, myDestYMin, myDestXMax, myDestYMax;
00270 
00271     destPointOnCPMatrix( theMatrixRow, myMatrixCol, &myDestXMin, &myDestYMin );
00272     destPointOnCPMatrix( theMatrixRow, myMatrixCol + 1, &myDestXMax, &myDestYMax );
00273 
00274     double xfrac = ( myDestX - myDestXMin ) / ( myDestXMax - myDestXMin );
00275 
00276     QgsPoint &mySrcPoint0 = mCPMatrix[theMatrixRow][myMatrixCol];
00277     QgsPoint &mySrcPoint1 = mCPMatrix[theMatrixRow][myMatrixCol+1];
00278     double s = mySrcPoint0.x() + ( mySrcPoint1.x() - mySrcPoint0.x() ) * xfrac;
00279     double t = mySrcPoint0.y() + ( mySrcPoint1.y() - mySrcPoint0.y() ) * xfrac;
00280 
00281     thePoints[myDestCol].setX( s );
00282     thePoints[myDestCol].setY( t );
00283   }
00284 }
00285 void QgsRasterProjector::nextHelper()
00286 {
00287   QgsPoint *tmp;
00288   tmp = pHelperTop;
00289   pHelperTop = pHelperBottom;
00290   pHelperBottom = tmp;
00291   calcHelper( mHelperTopRow + 2, pHelperBottom );
00292   mHelperTopRow++;
00293 }
00294 
00295 void QgsRasterProjector::srcRowCol( int theDestRow, int theDestCol, int *theSrcRow, int *theSrcCol )
00296 {
00297   if ( mApproximate ) approximateSrcRowCol( theDestRow, theDestCol, theSrcRow, theSrcCol );
00298   else preciseSrcRowCol( theDestRow, theDestCol, theSrcRow, theSrcCol );
00299 }
00300 
00301 void QgsRasterProjector::preciseSrcRowCol( int theDestRow, int theDestCol, int *theSrcRow, int *theSrcCol )
00302 {
00303   //QgsDebugMsg( QString( "theDestRow = %1" ).arg(theDestRow) );
00304   //QgsDebugMsg( QString( "theDestRow = %1 mDestExtent.yMaximum() = %2 mDestYRes = %3" ).arg(theDestRow).arg(mDestExtent.yMaximum()).arg(mDestYRes) );
00305 
00306   // Get coordinate of center of destination cell
00307   double x = mDestExtent.xMinimum() + ( theDestCol + 0.5 ) * mDestXRes;
00308   double y = mDestExtent.yMaximum() - ( theDestRow + 0.5 ) * mDestYRes;
00309   double z = 0;
00310 
00311   //QgsDebugMsg( QString( "x = %1 y = %2" ).arg(x).arg(y) );
00312   mCoordinateTransform.transformInPlace( x, y, z );
00313   //QgsDebugMsg( QString( "x = %1 y = %2" ).arg(x).arg(y) );
00314 
00315   // Get source row col
00316   *theSrcRow = ( int ) floor(( mSrcExtent.yMaximum() - y ) / mSrcYRes );
00317   *theSrcCol = ( int ) floor(( x - mSrcExtent.xMinimum() ) / mSrcXRes );
00318   //QgsDebugMsg( QString( "mSrcExtent.yMaximum() = %1 mSrcYRes = %2" ).arg(mSrcExtent.yMaximum()).arg(mSrcYRes) );
00319   //QgsDebugMsg( QString( "theSrcRow = %1 theSrcCol = %2" ).arg(*theSrcRow).arg(*theSrcCol) );
00320 
00321   // With epsg 32661 (Polar Stereographic) it was happening that *theSrcCol == mSrcCols
00322   // For now silently correct limits to avoid crashes
00323   // TODO: review
00324   if ( *theSrcRow >= mSrcRows ) *theSrcRow = mSrcRows - 1;
00325   if ( *theSrcRow < 0 ) *theSrcRow = 0;
00326   if ( *theSrcCol >= mSrcCols ) *theSrcCol = mSrcCols - 1;
00327   if ( *theSrcCol < 0 ) *theSrcCol = 0;
00328 
00329   assert( *theSrcRow < mSrcRows );
00330   assert( *theSrcCol < mSrcCols );
00331 }
00332 
00333 void QgsRasterProjector::approximateSrcRowCol( int theDestRow, int theDestCol, int *theSrcRow, int *theSrcCol )
00334 {
00335   int myMatrixRow = matrixRow( theDestRow );
00336   int myMatrixCol = matrixCol( theDestCol );
00337 
00338   if ( myMatrixRow > mHelperTopRow )
00339   {
00340     // TODO: make it more robust (for random, not sequential reading)
00341     nextHelper();
00342   }
00343 
00344   double myDestY = mDestExtent.yMaximum() - ( theDestRow + 0.5 ) * mDestYRes;
00345 
00346   // See the schema in javax.media.jai.WarpGrid doc (but up side down)
00347   // TODO: use some kind of cache of values which can be reused
00348   double myDestXMin, myDestYMin, myDestXMax, myDestYMax;
00349 
00350   destPointOnCPMatrix( myMatrixRow + 1, myMatrixCol, &myDestXMin, &myDestYMin );
00351   destPointOnCPMatrix( myMatrixRow, myMatrixCol + 1, &myDestXMax, &myDestYMax );
00352 
00353   double yfrac = ( myDestY - myDestYMin ) / ( myDestYMax - myDestYMin );
00354 
00355   QgsPoint &myTop = pHelperTop[theDestCol];
00356   QgsPoint &myBot = pHelperBottom[theDestCol];
00357 
00358   // Warning: this is very SLOW compared to the following code!:
00359   //double mySrcX = myBot.x() + (myTop.x() - myBot.x()) * yfrac;
00360   //double mySrcY = myBot.y() + (myTop.y() - myBot.y()) * yfrac;
00361 
00362   double tx = myTop.x();
00363   double ty = myTop.y();
00364   double bx = myBot.x();
00365   double by = myBot.y();
00366   double mySrcX = bx + ( tx - bx ) * yfrac;
00367   double mySrcY = by + ( ty - by ) * yfrac;
00368 
00369   // TODO: check again cell selection (coor is in the middle)
00370 
00371   *theSrcRow = ( int ) floor(( mSrcExtent.yMaximum() - mySrcY ) / mSrcYRes );
00372   *theSrcCol = ( int ) floor(( mySrcX - mSrcExtent.xMinimum() ) / mSrcXRes );
00373 
00374   // For now silently correct limits to avoid crashes
00375   // TODO: review
00376   if ( *theSrcRow >= mSrcRows ) *theSrcRow = mSrcRows - 1;
00377   if ( *theSrcRow < 0 ) *theSrcRow = 0;
00378   if ( *theSrcCol >= mSrcCols ) *theSrcCol = mSrcCols - 1;
00379   if ( *theSrcCol < 0 ) *theSrcCol = 0;
00380   assert( *theSrcRow < mSrcRows );
00381   assert( *theSrcCol < mSrcCols );
00382 }
00383 
00384 void QgsRasterProjector::insertRows()
00385 {
00386   for ( int r = 0; r < mCPRows - 1; r++ )
00387   {
00388     QList<QgsPoint> myRow;
00389     for ( int c = 0; c < mCPCols; c++ )
00390     {
00391       myRow.append( QgsPoint() );
00392     }
00393     QgsDebugMsgLevel( QString( "insert new row at %1" ).arg( 1 + r*2 ), 3 );
00394     mCPMatrix.insert( 1 + r*2,  myRow );
00395   }
00396   mCPRows += mCPRows - 1;
00397   for ( int r = 1; r < mCPRows - 1; r += 2 )
00398   {
00399     calcRow( r );
00400   }
00401 }
00402 
00403 void QgsRasterProjector::insertCols()
00404 {
00405   for ( int r = 0; r < mCPRows; r++ )
00406   {
00407     QList<QgsPoint> myRow;
00408     for ( int c = 0; c < mCPCols - 1; c++ )
00409     {
00410       mCPMatrix[r].insert( 1 + c*2,  QgsPoint() );
00411     }
00412   }
00413   mCPCols += mCPCols - 1;
00414   for ( int c = 1; c < mCPCols - 1; c += 2 )
00415   {
00416     calcCol( c );
00417   }
00418 
00419 }
00420 
00421 void QgsRasterProjector::calcCP( int theRow, int theCol )
00422 {
00423   double myDestX, myDestY;
00424   destPointOnCPMatrix( theRow, theCol, &myDestX, &myDestY );
00425   QgsPoint myDestPoint( myDestX, myDestY );
00426 
00427   mCPMatrix[theRow][theCol] = mCoordinateTransform.transform( myDestPoint );
00428 }
00429 
00430 bool QgsRasterProjector::calcRow( int theRow )
00431 {
00432   QgsDebugMsgLevel( QString( "theRow = %1" ).arg( theRow ), 3 );
00433   for ( int i = 0; i < mCPCols; i++ )
00434   {
00435     calcCP( theRow, i );
00436   }
00437 
00438   return true;
00439 }
00440 
00441 bool QgsRasterProjector::calcCol( int theCol )
00442 {
00443   QgsDebugMsgLevel( QString( "theCol = %1" ).arg( theCol ), 3 );
00444   for ( int i = 0; i < mCPRows; i++ )
00445   {
00446     calcCP( i, theCol );
00447   }
00448 
00449   return true;
00450 }
00451 
00452 bool QgsRasterProjector::checkCols()
00453 {
00454   for ( int c = 0; c < mCPCols; c++ )
00455   {
00456     for ( int r = 1; r < mCPRows - 1; r += 2 )
00457     {
00458       double myDestX, myDestY;
00459       destPointOnCPMatrix( r, c, &myDestX, &myDestY );
00460       QgsPoint myDestPoint( myDestX, myDestY );
00461 
00462       QgsPoint mySrcPoint1 = mCPMatrix[r-1][c];
00463       QgsPoint mySrcPoint2 = mCPMatrix[r][c];
00464       QgsPoint mySrcPoint3 = mCPMatrix[r+1][c];
00465 
00466       QgsPoint mySrcApprox(( mySrcPoint1.x() + mySrcPoint3.x() ) / 2, ( mySrcPoint1.y() + mySrcPoint3.y() ) / 2 );
00467       QgsPoint myDestApprox = mCoordinateTransform.transform( mySrcApprox, QgsCoordinateTransform::ReverseTransform );
00468       double mySqrDist = myDestApprox.sqrDist( myDestPoint );
00469       if ( mySqrDist > mSqrTolerance ) { return false; }
00470     }
00471   }
00472   return true;
00473 }
00474 
00475 bool QgsRasterProjector::checkRows()
00476 {
00477   for ( int r = 0; r < mCPRows; r++ )
00478   {
00479     for ( int c = 1; c < mCPCols - 1; c += 2 )
00480     {
00481       double myDestX, myDestY;
00482       destPointOnCPMatrix( r, c, &myDestX, &myDestY );
00483 
00484       QgsPoint myDestPoint( myDestX, myDestY );
00485       QgsPoint mySrcPoint1 = mCPMatrix[r][c-1];
00486       QgsPoint mySrcPoint2 = mCPMatrix[r][c];
00487       QgsPoint mySrcPoint3 = mCPMatrix[r][c+1];
00488 
00489       QgsPoint mySrcApprox(( mySrcPoint1.x() + mySrcPoint3.x() ) / 2, ( mySrcPoint1.y() + mySrcPoint3.y() ) / 2 );
00490       QgsPoint myDestApprox = mCoordinateTransform.transform( mySrcApprox, QgsCoordinateTransform::ReverseTransform );
00491       double mySqrDist = myDestApprox.sqrDist( myDestPoint );
00492       if ( mySqrDist > mSqrTolerance ) { return false; }
00493     }
00494   }
00495   return true;
00496 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines