38   projector->mSrcCRS = mSrcCRS;
 
   39   projector->mDestCRS = mDestCRS;
 
   40   projector->mTransformContext = mTransformContext;
 
   43   projector->mSrcDatumTransform = mSrcDatumTransform;
 
   44   projector->mDestDatumTransform = mDestDatumTransform;
 
   47   projector->mPrecision = mPrecision;
 
   70                                  int srcDatumTransform,
 
   71                                  int destDatumTransform )
 
   76   mSrcDatumTransform = srcDatumTransform;
 
   77   mDestDatumTransform = destDatumTransform;
 
   85   mTransformContext = transformContext;
 
   87   mSrcDatumTransform = -1;
 
   88   mDestDatumTransform = -1;
 
   94   : mApproximate( false )
 
   95   , mInverseCt( inverseCt )
 
   96   , mDestExtent( extent )
 
  105   , mDestRowsPerMatrixRow( 0.0 )
 
  106   , mDestColsPerMatrixCol( 0.0 )
 
  110   , mSqrTolerance( 0.0 )
 
  128       if ( mExtent.isEmpty() )
 
  130         mExtent = provider->
extent();
 
  135   mDestXRes = mDestExtent.
width() / ( mDestCols );
 
  136   mDestYRes = mDestExtent.height() / ( mDestRows );
 
  142   double myDestRes = mDestXRes < mDestYRes ? mDestXRes : mDestYRes;
 
  143   mSqrTolerance = myDestRes * myDestRes;
 
  151     mApproximate = 
false;
 
  156   mCPCols = mCPRows = 3;
 
  157   for ( 
int i = 0; i < mCPRows; i++ )
 
  159     QList<QgsPointXY> myRow;
 
  163     mCPMatrix.insert( i, myRow );
 
  165     QList<bool> myLegalRow;
 
  166     myLegalRow.append( 
bool( 
false ) );
 
  167     myLegalRow.append( 
bool( 
false ) );
 
  168     myLegalRow.append( 
bool( 
false ) );
 
  169     mCPLegalMatrix.insert( i, myLegalRow );
 
  171   for ( 
int i = 0; i < mCPRows; i++ )
 
  173     calcRow( i, inverseCt );
 
  178     bool myColsOK = checkCols( inverseCt );
 
  181       insertRows( inverseCt );
 
  183     bool myRowsOK = checkRows( inverseCt );
 
  186       insertCols( inverseCt );
 
  188     if ( myColsOK && myRowsOK )
 
  195     if ( mCPRows * mCPCols > 0.25 * mDestRows * mDestCols )
 
  199       mApproximate = 
false;
 
  207   QgsDebugMsgLevel( QStringLiteral( 
"CPMatrix size: mCPRows = %1 mCPCols = %2" ).arg( mCPRows ).arg( mCPCols ), 4 );
 
  208   mDestRowsPerMatrixRow = 
static_cast< double >( mDestRows ) / ( mCPRows - 1 );
 
  209   mDestColsPerMatrixCol = 
static_cast< double >( mDestCols ) / ( mCPCols - 1 );
 
  219   calcHelper( 0, pHelperTop );
 
  220   calcHelper( 1, pHelperBottom );
 
  226   mSrcYRes = mSrcExtent.height() / mSrcRows;
 
  227   mSrcXRes = mSrcExtent.width() / mSrcCols;
 
  230 ProjectorData::~ProjectorData()
 
  233   delete[] pHelperBottom;
 
  237 void ProjectorData::calcSrcExtent()
 
  248   mSrcExtent = 
QgsRectangle( myPoint.
x(), myPoint.
y(), myPoint.
x(), myPoint.
y() );
 
  249   for ( 
int i = 0; i < mCPRows; i++ )
 
  251     for ( 
int j = 0; j < mCPCols ; j++ )
 
  253       myPoint = mCPMatrix[i][j];
 
  254       if ( mCPLegalMatrix[i][j] )
 
  256         mSrcExtent.combineExtentWith( myPoint.
x(), myPoint.
y() );
 
  263   mSrcExtent = mSrcExtent.intersect( mExtent );
 
  274   if ( !mExtent.isEmpty() )
 
  276     if ( mMaxSrcXRes > 0 )
 
  279       double col = std::floor( ( mSrcExtent.xMinimum() - mExtent.xMinimum() ) / mMaxSrcXRes );
 
  280       double x = mExtent.xMinimum() + col * mMaxSrcXRes;
 
  281       mSrcExtent.setXMinimum( x );
 
  283       col = std::ceil( ( mSrcExtent.xMaximum() - mExtent.xMinimum() ) / mMaxSrcXRes );
 
  284       x = mExtent.xMinimum() + col * mMaxSrcXRes;
 
  285       mSrcExtent.setXMaximum( x );
 
  287     if ( mMaxSrcYRes > 0 )
 
  289       double row = std::floor( ( mExtent.yMaximum() - mSrcExtent.yMaximum() ) / mMaxSrcYRes );
 
  290       double y = mExtent.yMaximum() - row * mMaxSrcYRes;
 
  291       mSrcExtent.setYMaximum( y );
 
  293       row = std::ceil( ( mExtent.yMaximum() - mSrcExtent.yMinimum() ) / mMaxSrcYRes );
 
  294       y = mExtent.yMaximum() - row * mMaxSrcYRes;
 
  295       mSrcExtent.setYMinimum( y );
 
  301 QString ProjectorData::cpToString()
 
  304   for ( 
int i = 0; i < mCPRows; i++ )
 
  308     for ( 
int j = 0; j < mCPCols; j++ )
 
  311         myString += QLatin1String( 
"  " );
 
  313       if ( mCPLegalMatrix[i][j] )
 
  319         myString += QLatin1String( 
"(-,-)" );
 
  326 void ProjectorData::calcSrcRowsCols()
 
  334   double myMinSize = std::numeric_limits<double>::max();
 
  339     double myDestColsPerMatrixCell = 
static_cast< double >( mDestCols ) / mCPCols;
 
  340     double myDestRowsPerMatrixCell = 
static_cast< double >( mDestRows ) / mCPRows;
 
  341     QgsDebugMsgLevel( QStringLiteral( 
"myDestColsPerMatrixCell = %1 myDestRowsPerMatrixCell = %2" ).arg( myDestColsPerMatrixCell ).arg( myDestRowsPerMatrixCell ), 4 );
 
  342     for ( 
int i = 0; i < mCPRows - 1; i++ )
 
  344       for ( 
int j = 0; j < mCPCols - 1; j++ )
 
  349         if ( mCPLegalMatrix[i][j] && mCPLegalMatrix[i][j + 1] && mCPLegalMatrix[i + 1][j] )
 
  351           double mySize = std::sqrt( myPointA.
sqrDist( myPointB ) ) / myDestColsPerMatrixCell;
 
  352           if ( mySize < myMinSize )
 
  355           mySize = std::sqrt( myPointA.
sqrDist( myPointC ) ) / myDestRowsPerMatrixCell;
 
  356           if ( mySize < myMinSize )
 
  367     int srcXSize, srcYSize;
 
  370       double srcXRes = srcExtent.
width() / srcXSize;
 
  371       double srcYRes = srcExtent.
height() / srcYSize;
 
  372       myMinSize = std::min( srcXRes, srcYRes );
 
  376       QgsDebugMsg( QStringLiteral( 
"Cannot get src extent/size" ) );
 
  385   QgsDebugMsgLevel( QStringLiteral( 
"mMaxSrcXRes = %1 mMaxSrcYRes = %2" ).arg( mMaxSrcXRes ).arg( mMaxSrcYRes ), 4 );
 
  387   double myMinXSize = mMaxSrcXRes > myMinSize ? mMaxSrcXRes : myMinSize;
 
  388   double myMinYSize = mMaxSrcYRes > myMinSize ? mMaxSrcYRes : myMinSize;
 
  389   QgsDebugMsgLevel( QStringLiteral( 
"myMinXSize = %1 myMinYSize = %2" ).arg( myMinXSize ).arg( myMinYSize ), 4 );
 
  390   QgsDebugMsgLevel( QStringLiteral( 
"mSrcExtent.width = %1 mSrcExtent.height = %2" ).arg( mSrcExtent.width() ).arg( mSrcExtent.height() ), 4 );
 
  393   mSrcRows = 
static_cast< int >( std::round( mSrcExtent.height() / myMinYSize ) );
 
  394   mSrcCols = 
static_cast< int >( std::round( mSrcExtent.width() / myMinXSize ) );
 
  396   QgsDebugMsgLevel( QStringLiteral( 
"mSrcRows = %1 mSrcCols = %2" ).arg( mSrcRows ).arg( mSrcCols ), 4 );
 
  400 inline void ProjectorData::destPointOnCPMatrix( 
int row, 
int col, 
double *theX, 
double *theY )
 
  402   *theX = mDestExtent.xMinimum() + col * mDestExtent.width() / ( mCPCols - 1 );
 
  403   *theY = mDestExtent.yMaximum() - row * mDestExtent.height() / ( mCPRows - 1 );
 
  406 inline int ProjectorData::matrixRow( 
int destRow )
 
  408   return static_cast< int >( std::floor( ( destRow + 0.5 ) / mDestRowsPerMatrixRow ) );
 
  410 inline int ProjectorData::matrixCol( 
int destCol )
 
  412   return static_cast< int >( std::floor( ( destCol + 0.5 ) / mDestColsPerMatrixCol ) );
 
  415 void ProjectorData::calcHelper( 
int matrixRow, 
QgsPointXY *points )
 
  418   for ( 
int myDestCol = 0; myDestCol < mDestCols; myDestCol++ )
 
  420     double myDestX = mDestExtent.xMinimum() + ( myDestCol + 0.5 ) * mDestXRes;
 
  422     int myMatrixCol = matrixCol( myDestCol );
 
  424     double myDestXMin, myDestYMin, myDestXMax, myDestYMax;
 
  426     destPointOnCPMatrix( matrixRow, myMatrixCol, &myDestXMin, &myDestYMin );
 
  427     destPointOnCPMatrix( matrixRow, myMatrixCol + 1, &myDestXMax, &myDestYMax );
 
  429     double xfrac = ( myDestX - myDestXMin ) / ( myDestXMax - myDestXMin );
 
  431     QgsPointXY &mySrcPoint0 = mCPMatrix[matrixRow][myMatrixCol];
 
  432     QgsPointXY &mySrcPoint1 = mCPMatrix[matrixRow][myMatrixCol + 1];
 
  433     double s = mySrcPoint0.
x() + ( mySrcPoint1.
x() - mySrcPoint0.
x() ) * xfrac;
 
  434     double t = mySrcPoint0.
y() + ( mySrcPoint1.
y() - mySrcPoint0.
y() ) * xfrac;
 
  436     points[myDestCol].
setX( s );
 
  437     points[myDestCol].
setY( t );
 
  441 void ProjectorData::nextHelper()
 
  446   pHelperTop = pHelperBottom;
 
  448   calcHelper( mHelperTopRow + 2, pHelperBottom );
 
  452 bool ProjectorData::srcRowCol( 
int destRow, 
int destCol, 
int *srcRow, 
int *srcCol )
 
  456     return approximateSrcRowCol( destRow, destCol, srcRow, srcCol );
 
  460     return preciseSrcRowCol( destRow, destCol, srcRow, srcCol );
 
  464 bool ProjectorData::preciseSrcRowCol( 
int destRow, 
int destCol, 
int *srcRow, 
int *srcCol )
 
  466 #if 0 // too slow, even if we only run it on debug builds! 
  468   QgsDebugMsgLevel( QStringLiteral( 
"theDestRow = %1 mDestExtent.yMaximum() = %2 mDestYRes = %3" ).arg( destRow ).arg( mDestExtent.yMaximum() ).arg( mDestYRes ), 5 );
 
  472   double x = mDestExtent.xMinimum() + ( destCol + 0.5 ) * mDestXRes;
 
  473   double y = mDestExtent.yMaximum() - ( destRow + 0.5 ) * mDestYRes;
 
  480   if ( mInverseCt.isValid() )
 
  484       mInverseCt.transformInPlace( x, y, z );
 
  496   if ( !mExtent.contains( 
QgsPointXY( x, y ) ) )
 
  501   *srcRow = 
static_cast< int >( std::floor( ( mSrcExtent.yMaximum() - y ) / mSrcYRes ) );
 
  502   *srcCol = 
static_cast< int >( std::floor( ( x - mSrcExtent.xMinimum() ) / mSrcXRes ) );
 
  504   QgsDebugMsgLevel( QStringLiteral( 
"mSrcExtent.yMinimum() = %1 mSrcExtent.yMaximum() = %2 mSrcYRes = %3" ).arg( mSrcExtent.yMinimum() ).arg( mSrcExtent.yMaximum() ).arg( mSrcYRes ), 5 );
 
  505   QgsDebugMsgLevel( QStringLiteral( 
"theSrcRow = %1 srcCol = %2" ).arg( *srcRow ).arg( *srcCol ), 5 );
 
  512   if ( *srcRow >= mSrcRows ) 
return false;
 
  513   if ( *srcRow < 0 ) 
return false;
 
  514   if ( *srcCol >= mSrcCols ) 
return false;
 
  515   if ( *srcCol < 0 ) 
return false;
 
  520 bool ProjectorData::approximateSrcRowCol( 
int destRow, 
int destCol, 
int *srcRow, 
int *srcCol )
 
  522   int myMatrixRow = matrixRow( destRow );
 
  523   int myMatrixCol = matrixCol( destCol );
 
  525   if ( myMatrixRow > mHelperTopRow )
 
  531   double myDestY = mDestExtent.yMaximum() - ( destRow + 0.5 ) * mDestYRes;
 
  535   double myDestXMin, myDestYMin, myDestXMax, myDestYMax;
 
  537   destPointOnCPMatrix( myMatrixRow + 1, myMatrixCol, &myDestXMin, &myDestYMin );
 
  538   destPointOnCPMatrix( myMatrixRow, myMatrixCol + 1, &myDestXMax, &myDestYMax );
 
  540   double yfrac = ( myDestY - myDestYMin ) / ( myDestYMax - myDestYMin );
 
  549   double tx = myTop.
x();
 
  550   double ty = myTop.
y();
 
  551   double bx = myBot.
x();
 
  552   double by = myBot.
y();
 
  553   double mySrcX = bx + ( tx - bx ) * yfrac;
 
  554   double mySrcY = by + ( ty - by ) * yfrac;
 
  556   if ( !mExtent.contains( 
QgsPointXY( mySrcX, mySrcY ) ) )
 
  563   *srcRow = 
static_cast< int >( std::floor( ( mSrcExtent.yMaximum() - mySrcY ) / mSrcYRes ) );
 
  564   *srcCol = 
static_cast< int >( std::floor( ( mySrcX - mSrcExtent.xMinimum() ) / mSrcXRes ) );
 
  569   if ( *srcRow >= mSrcRows ) 
return false;
 
  570   if ( *srcRow < 0 ) 
return false;
 
  571   if ( *srcCol >= mSrcCols ) 
return false;
 
  572   if ( *srcCol < 0 ) 
return false;
 
  579   for ( 
int r = 0; r < mCPRows - 1; r++ )
 
  581     QList<QgsPointXY> myRow;
 
  582     QList<bool> myLegalRow;
 
  583     myRow.reserve( mCPCols );
 
  584     myLegalRow.reserve( mCPCols );
 
  585     for ( 
int c = 0; 
c < mCPCols; ++
c )
 
  588       myLegalRow.append( 
false );
 
  590     QgsDebugMsgLevel( QStringLiteral( 
"insert new row at %1" ).arg( 1 + r * 2 ), 3 );
 
  591     mCPMatrix.insert( 1 + r * 2, myRow );
 
  592     mCPLegalMatrix.insert( 1 + r * 2, myLegalRow );
 
  594   mCPRows += mCPRows - 1;
 
  595   for ( 
int r = 1; r < mCPRows - 1; r += 2 )
 
  603   for ( 
int r = 0; r < mCPRows; r++ )
 
  605     for ( 
int c = 0; 
c < mCPCols - 1; 
c++ )
 
  608       mCPLegalMatrix[r].insert( 1 + 
c * 2, 
false );
 
  611   mCPCols += mCPCols - 1;
 
  612   for ( 
int c = 1; 
c < mCPCols - 1; 
c += 2 )
 
  621   double myDestX, myDestY;
 
  622   destPointOnCPMatrix( row, col, &myDestX, &myDestY );
 
  628       mCPMatrix[row][col] = ct.
transform( myDestPoint );
 
  629       mCPLegalMatrix[row][col] = 
true;
 
  633       mCPLegalMatrix[row][col] = 
false;
 
  640     mCPLegalMatrix[row][col] = 
false;
 
  647   for ( 
int i = 0; i < mCPCols; i++ )
 
  649     calcCP( row, i, ct );
 
  658   for ( 
int i = 0; i < mCPRows; i++ )
 
  660     calcCP( i, col, ct );
 
  673   for ( 
int c = 0; 
c < mCPCols; 
c++ )
 
  675     for ( 
int r = 1; r < mCPRows - 1; r += 2 )
 
  677       double myDestX, myDestY;
 
  678       destPointOnCPMatrix( r, 
c, &myDestX, &myDestY );
 
  685       QgsPointXY mySrcApprox( ( mySrcPoint1.
x() + mySrcPoint3.
x() ) / 2, ( mySrcPoint1.
y() + mySrcPoint3.
y() ) / 2 );
 
  686       if ( !mCPLegalMatrix[r - 1][
c] || !mCPLegalMatrix[r][
c] || !mCPLegalMatrix[r + 1][
c] )
 
  694         double mySqrDist = myDestApprox.
sqrDist( myDestPoint );
 
  695         if ( mySqrDist > mSqrTolerance )
 
  718   for ( 
int r = 0; r < mCPRows; r++ )
 
  720     for ( 
int c = 1; 
c < mCPCols - 1; 
c += 2 )
 
  722       double myDestX, myDestY;
 
  723       destPointOnCPMatrix( r, 
c, &myDestX, &myDestY );
 
  730       QgsPointXY mySrcApprox( ( mySrcPoint1.
x() + mySrcPoint3.
x() ) / 2, ( mySrcPoint1.
y() + mySrcPoint3.
y() ) / 2 );
 
  731       if ( !mCPLegalMatrix[r][
c - 1] || !mCPLegalMatrix[r][
c] || !mCPLegalMatrix[r][
c + 1] )
 
  739         double mySqrDist = myDestApprox.
sqrDist( myDestPoint );
 
  740         if ( mySqrDist > mSqrTolerance )
 
  764       return tr( 
"Approximate" );
 
  766       return tr( 
"Exact" );
 
  768   return QStringLiteral( 
"Unknown" );
 
  774   QgsDebugMsgLevel( QStringLiteral( 
"width = %1 height = %2" ).arg( width ).arg( height ), 4 );
 
  784   if ( ! mSrcCRS.
isValid() || ! mDestCRS.
isValid() || mSrcCRS == mDestCRS )
 
  795   ProjectorData pd( 
extent, width, height, 
mInput, inverseCt, mPrecision, feedback );
 
  800   QgsDebugMsgLevel( QStringLiteral( 
"srcExtent:\n%1" ).arg( pd.srcExtent().toString() ), 4 );
 
  801   QgsDebugMsgLevel( QStringLiteral( 
"srcCols = %1 srcRows = %2" ).arg( pd.srcCols() ).arg( pd.srcRows() ), 4 );
 
  804   if ( pd.srcRows() <= 0 || pd.srcCols() <= 0 )
 
  810   std::unique_ptr< QgsRasterBlock > inputBlock( 
mInput->
block( bandNo, pd.srcExtent(), pd.srcCols(), pd.srcRows(), feedback ) );
 
  811   if ( !inputBlock || inputBlock->isEmpty() )
 
  813     QgsDebugMsg( QStringLiteral( 
"No raster data!" ) );
 
  819   std::unique_ptr< QgsRasterBlock > outputBlock( 
new QgsRasterBlock( inputBlock->dataType(), width, height ) );
 
  820   if ( inputBlock->hasNoDataValue() )
 
  822     outputBlock->setNoDataValue( inputBlock->noDataValue() );
 
  824   if ( !outputBlock->isValid() )
 
  826     QgsDebugMsg( QStringLiteral( 
"Cannot create block" ) );
 
  827     return outputBlock.release();
 
  831   outputBlock->setIsNoData();
 
  845   outputBlock->setIsNoData();
 
  848   for ( 
int i = 0; i < height; ++i )
 
  852     for ( 
int j = 0; j < width; ++j )
 
  854       bool inside = pd.srcRowCol( i, j, &srcRow, &srcCol );
 
  855       if ( !inside ) 
continue; 
 
  857       qgssize srcIndex = 
static_cast< qgssize >( srcRow * pd.srcCols() + srcCol );
 
  860       if ( doNoData && inputBlock->isNoData( srcRow, srcCol ) )
 
  862         outputBlock->setIsNoData( i, j );
 
  867       char *srcBits = inputBlock->bits( srcIndex );
 
  868       char *destBits = outputBlock->bits( destIndex );
 
  879       memcpy( destBits, srcBits, pixelSize );
 
  880       outputBlock->setIsData( i, j );
 
  884   return outputBlock.release();
 
  888     QgsRectangle &destExtent, 
int &destXSize, 
int &destYSize )
 
  890   if ( srcExtent.
isEmpty() || srcXSize <= 0 || srcYSize <= 0 )
 
  900   return extentSize( ct, srcExtent, srcXSize, srcYSize, destExtent, destXSize, destYSize );
 
  904                                      const QgsRectangle &srcExtent, 
int srcXSize, 
int srcYSize,
 
  905                                      QgsRectangle &destExtent, 
int &destXSize, 
int &destYSize )
 
  907   if ( srcExtent.
isEmpty() || srcXSize <= 0 || srcYSize <= 0 )
 
  916   double srcXStep = srcExtent.
width() / 3;
 
  917   double srcYStep = srcExtent.
height() / 3;
 
  918   double srcXRes = srcExtent.
width() / srcXSize;
 
  919   double srcYRes = srcExtent.
height() / srcYSize;
 
  920   double destXRes = std::numeric_limits<double>::max();
 
  921   double destYRes = std::numeric_limits<double>::max();
 
  923   for ( 
int i = 0; i < 3; i++ )
 
  925     double x = srcExtent.
xMinimum() + i * srcXStep;
 
  926     for ( 
int j = 0; j < 3; j++ )
 
  928       double y = srcExtent.
yMinimum() + j * srcYStep;
 
  929       QgsRectangle srcRectangle( x - srcXRes / 2, y - srcYRes / 2, x + srcXRes / 2, y + srcYRes / 2 );
 
  933         if ( destRectangle.
width() > 0 )
 
  935           destXRes = std::min( destXRes, destRectangle.
width() );
 
  937         if ( destRectangle.
height() > 0 )
 
  939           destYRes = std::min( destYRes, destRectangle.
height() );
 
  948   destXSize = std::max( 1, 
static_cast< int >( destExtent.
width() / destYRes ) );
 
  949   destYSize = std::max( 1, 
static_cast< int >( destExtent.
height() / destYRes ) );