Quantum GIS API Documentation
1.7.4
|
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 }