|
QGIS API Documentation
master-6227475
|
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 }