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 )
133 if ( mExtent.isEmpty() )
135 mExtent = provider->
extent();
140 mDestXRes = mDestExtent.
width() / ( mDestCols );
141 mDestYRes = mDestExtent.height() / ( mDestRows );
147 double myDestRes = mDestXRes < mDestYRes ? mDestXRes : mDestYRes;
148 mSqrTolerance = myDestRes * myDestRes;
156 mApproximate =
false;
161 mCPCols = mCPRows = 3;
162 for (
int i = 0; i < mCPRows; i++ )
164 QList<QgsPointXY> myRow;
168 mCPMatrix.insert( i, myRow );
170 QList<bool> myLegalRow;
171 myLegalRow.append(
bool(
false ) );
172 myLegalRow.append(
bool(
false ) );
173 myLegalRow.append(
bool(
false ) );
174 mCPLegalMatrix.insert( i, myLegalRow );
176 for (
int i = 0; i < mCPRows; i++ )
178 calcRow( i, inverseCt );
183 bool myColsOK = checkCols( inverseCt );
186 insertRows( inverseCt );
188 bool myRowsOK = checkRows( inverseCt );
191 insertCols( inverseCt );
193 if ( myColsOK && myRowsOK )
200 if ( mCPRows * mCPCols > 0.25 * mDestRows * mDestCols )
204 mApproximate =
false;
212 QgsDebugMsgLevel( QStringLiteral(
"CPMatrix size: mCPRows = %1 mCPCols = %2" ).arg( mCPRows ).arg( mCPCols ), 4 );
213 mDestRowsPerMatrixRow =
static_cast< double >( mDestRows ) / ( mCPRows - 1 );
214 mDestColsPerMatrixCol =
static_cast< double >( mDestCols ) / ( mCPCols - 1 );
224 calcHelper( 0, pHelperTop );
225 calcHelper( 1, pHelperBottom );
231 mSrcYRes = mSrcExtent.height() / mSrcRows;
232 mSrcXRes = mSrcExtent.width() / mSrcCols;
235 ProjectorData::~ProjectorData()
238 delete[] pHelperBottom;
242 void ProjectorData::calcSrcExtent()
253 mSrcExtent =
QgsRectangle( myPoint.
x(), myPoint.
y(), myPoint.
x(), myPoint.
y() );
254 for (
int i = 0; i < mCPRows; i++ )
256 for (
int j = 0; j < mCPCols ; j++ )
258 myPoint = mCPMatrix[i][j];
259 if ( mCPLegalMatrix[i][j] )
261 mSrcExtent.combineExtentWith( myPoint.
x(), myPoint.
y() );
268 mSrcExtent = mSrcExtent.intersect( mExtent );
279 if ( !mExtent.isEmpty() )
281 if ( mMaxSrcXRes > 0 )
284 double col = std::floor( ( mSrcExtent.xMinimum() - mExtent.xMinimum() ) / mMaxSrcXRes );
285 double x = mExtent.xMinimum() + col * mMaxSrcXRes;
286 mSrcExtent.setXMinimum( x );
288 col = std::ceil( ( mSrcExtent.xMaximum() - mExtent.xMinimum() ) / mMaxSrcXRes );
289 x = mExtent.xMinimum() + col * mMaxSrcXRes;
290 mSrcExtent.setXMaximum( x );
292 if ( mMaxSrcYRes > 0 )
294 double row = std::floor( ( mExtent.yMaximum() - mSrcExtent.yMaximum() ) / mMaxSrcYRes );
295 double y = mExtent.yMaximum() - row * mMaxSrcYRes;
296 mSrcExtent.setYMaximum( y );
298 row = std::ceil( ( mExtent.yMaximum() - mSrcExtent.yMinimum() ) / mMaxSrcYRes );
299 y = mExtent.yMaximum() - row * mMaxSrcYRes;
300 mSrcExtent.setYMinimum( y );
306 QString ProjectorData::cpToString()
309 for (
int i = 0; i < mCPRows; i++ )
313 for (
int j = 0; j < mCPCols; j++ )
316 myString += QLatin1String(
" " );
318 if ( mCPLegalMatrix[i][j] )
324 myString += QLatin1String(
"(-,-)" );
331 void ProjectorData::calcSrcRowsCols()
339 double myMinSize = std::numeric_limits<double>::max();
344 double myDestColsPerMatrixCell =
static_cast< double >( mDestCols ) / mCPCols;
345 double myDestRowsPerMatrixCell =
static_cast< double >( mDestRows ) / mCPRows;
346 QgsDebugMsgLevel( QStringLiteral(
"myDestColsPerMatrixCell = %1 myDestRowsPerMatrixCell = %2" ).arg( myDestColsPerMatrixCell ).arg( myDestRowsPerMatrixCell ), 4 );
347 for (
int i = 0; i < mCPRows - 1; i++ )
349 for (
int j = 0; j < mCPCols - 1; j++ )
354 if ( mCPLegalMatrix[i][j] && mCPLegalMatrix[i][j + 1] && mCPLegalMatrix[i + 1][j] )
356 double mySize = std::sqrt( myPointA.
sqrDist( myPointB ) ) / myDestColsPerMatrixCell;
357 if ( mySize < myMinSize )
360 mySize = std::sqrt( myPointA.
sqrDist( myPointC ) ) / myDestRowsPerMatrixCell;
361 if ( mySize < myMinSize )
372 int srcXSize, srcYSize;
375 double srcXRes = srcExtent.
width() / srcXSize;
376 double srcYRes = srcExtent.
height() / srcYSize;
377 myMinSize = std::min( srcXRes, srcYRes );
381 QgsDebugMsg( QStringLiteral(
"Cannot get src extent/size" ) );
390 QgsDebugMsgLevel( QStringLiteral(
"mMaxSrcXRes = %1 mMaxSrcYRes = %2" ).arg( mMaxSrcXRes ).arg( mMaxSrcYRes ), 4 );
392 double myMinXSize = mMaxSrcXRes > myMinSize ? mMaxSrcXRes : myMinSize;
393 double myMinYSize = mMaxSrcYRes > myMinSize ? mMaxSrcYRes : myMinSize;
394 QgsDebugMsgLevel( QStringLiteral(
"myMinXSize = %1 myMinYSize = %2" ).arg( myMinXSize ).arg( myMinYSize ), 4 );
395 QgsDebugMsgLevel( QStringLiteral(
"mSrcExtent.width = %1 mSrcExtent.height = %2" ).arg( mSrcExtent.width() ).arg( mSrcExtent.height() ), 4 );
398 mSrcRows =
static_cast< int >( std::round( mSrcExtent.height() / myMinYSize ) );
399 mSrcCols =
static_cast< int >( std::round( mSrcExtent.width() / myMinXSize ) );
401 QgsDebugMsgLevel( QStringLiteral(
"mSrcRows = %1 mSrcCols = %2" ).arg( mSrcRows ).arg( mSrcCols ), 4 );
405 inline void ProjectorData::destPointOnCPMatrix(
int row,
int col,
double *theX,
double *theY )
407 *theX = mDestExtent.xMinimum() + col * mDestExtent.width() / ( mCPCols - 1 );
408 *theY = mDestExtent.yMaximum() - row * mDestExtent.height() / ( mCPRows - 1 );
411 inline int ProjectorData::matrixRow(
int destRow )
413 return static_cast< int >( std::floor( ( destRow + 0.5 ) / mDestRowsPerMatrixRow ) );
415 inline int ProjectorData::matrixCol(
int destCol )
417 return static_cast< int >( std::floor( ( destCol + 0.5 ) / mDestColsPerMatrixCol ) );
420 void ProjectorData::calcHelper(
int matrixRow,
QgsPointXY *points )
423 for (
int myDestCol = 0; myDestCol < mDestCols; myDestCol++ )
425 double myDestX = mDestExtent.xMinimum() + ( myDestCol + 0.5 ) * mDestXRes;
427 int myMatrixCol = matrixCol( myDestCol );
429 double myDestXMin, myDestYMin, myDestXMax, myDestYMax;
431 destPointOnCPMatrix( matrixRow, myMatrixCol, &myDestXMin, &myDestYMin );
432 destPointOnCPMatrix( matrixRow, myMatrixCol + 1, &myDestXMax, &myDestYMax );
434 double xfrac = ( myDestX - myDestXMin ) / ( myDestXMax - myDestXMin );
436 QgsPointXY &mySrcPoint0 = mCPMatrix[matrixRow][myMatrixCol];
437 QgsPointXY &mySrcPoint1 = mCPMatrix[matrixRow][myMatrixCol + 1];
438 double s = mySrcPoint0.
x() + ( mySrcPoint1.
x() - mySrcPoint0.
x() ) * xfrac;
439 double t = mySrcPoint0.
y() + ( mySrcPoint1.
y() - mySrcPoint0.
y() ) * xfrac;
441 points[myDestCol].
setX( s );
442 points[myDestCol].
setY( t );
446 void ProjectorData::nextHelper()
451 pHelperTop = pHelperBottom;
453 calcHelper( mHelperTopRow + 2, pHelperBottom );
457 bool ProjectorData::srcRowCol(
int destRow,
int destCol,
int *srcRow,
int *srcCol )
461 return approximateSrcRowCol( destRow, destCol, srcRow, srcCol );
465 return preciseSrcRowCol( destRow, destCol, srcRow, srcCol );
469 bool ProjectorData::preciseSrcRowCol(
int destRow,
int destCol,
int *srcRow,
int *srcCol )
471 #if 0 // too slow, even if we only run it on debug builds!
473 QgsDebugMsgLevel( QStringLiteral(
"theDestRow = %1 mDestExtent.yMaximum() = %2 mDestYRes = %3" ).arg( destRow ).arg( mDestExtent.yMaximum() ).arg( mDestYRes ), 5 );
477 double x = mDestExtent.xMinimum() + ( destCol + 0.5 ) * mDestXRes;
478 double y = mDestExtent.yMaximum() - ( destRow + 0.5 ) * mDestYRes;
485 if ( mInverseCt.isValid() )
489 mInverseCt.transformInPlace( x, y, z );
501 if ( !mExtent.contains(
QgsPointXY( x, y ) ) )
506 *srcRow =
static_cast< int >( std::floor( ( mSrcExtent.yMaximum() - y ) / mSrcYRes ) );
507 *srcCol =
static_cast< int >( std::floor( ( x - mSrcExtent.xMinimum() ) / mSrcXRes ) );
509 QgsDebugMsgLevel( QStringLiteral(
"mSrcExtent.yMinimum() = %1 mSrcExtent.yMaximum() = %2 mSrcYRes = %3" ).arg( mSrcExtent.yMinimum() ).arg( mSrcExtent.yMaximum() ).arg( mSrcYRes ), 5 );
510 QgsDebugMsgLevel( QStringLiteral(
"theSrcRow = %1 srcCol = %2" ).arg( *srcRow ).arg( *srcCol ), 5 );
517 if ( *srcRow >= mSrcRows )
return false;
518 if ( *srcRow < 0 )
return false;
519 if ( *srcCol >= mSrcCols )
return false;
520 if ( *srcCol < 0 )
return false;
525 bool ProjectorData::approximateSrcRowCol(
int destRow,
int destCol,
int *srcRow,
int *srcCol )
527 int myMatrixRow = matrixRow( destRow );
528 int myMatrixCol = matrixCol( destCol );
530 if ( myMatrixRow > mHelperTopRow )
536 double myDestY = mDestExtent.yMaximum() - ( destRow + 0.5 ) * mDestYRes;
540 double myDestXMin, myDestYMin, myDestXMax, myDestYMax;
542 destPointOnCPMatrix( myMatrixRow + 1, myMatrixCol, &myDestXMin, &myDestYMin );
543 destPointOnCPMatrix( myMatrixRow, myMatrixCol + 1, &myDestXMax, &myDestYMax );
545 double yfrac = ( myDestY - myDestYMin ) / ( myDestYMax - myDestYMin );
554 double tx = myTop.
x();
555 double ty = myTop.
y();
556 double bx = myBot.
x();
557 double by = myBot.
y();
558 double mySrcX = bx + ( tx - bx ) * yfrac;
559 double mySrcY = by + ( ty - by ) * yfrac;
561 if ( !mExtent.contains(
QgsPointXY( mySrcX, mySrcY ) ) )
568 *srcRow =
static_cast< int >( std::floor( ( mSrcExtent.yMaximum() - mySrcY ) / mSrcYRes ) );
569 *srcCol =
static_cast< int >( std::floor( ( mySrcX - mSrcExtent.xMinimum() ) / mSrcXRes ) );
574 if ( *srcRow >= mSrcRows )
return false;
575 if ( *srcRow < 0 )
return false;
576 if ( *srcCol >= mSrcCols )
return false;
577 if ( *srcCol < 0 )
return false;
584 for (
int r = 0; r < mCPRows - 1; r++ )
586 QList<QgsPointXY> myRow;
587 QList<bool> myLegalRow;
588 myRow.reserve( mCPCols );
589 myLegalRow.reserve( mCPCols );
590 for (
int c = 0;
c < mCPCols; ++
c )
593 myLegalRow.append(
false );
595 QgsDebugMsgLevel( QStringLiteral(
"insert new row at %1" ).arg( 1 + r * 2 ), 3 );
596 mCPMatrix.insert( 1 + r * 2, myRow );
597 mCPLegalMatrix.insert( 1 + r * 2, myLegalRow );
599 mCPRows += mCPRows - 1;
600 for (
int r = 1; r < mCPRows - 1; r += 2 )
608 for (
int r = 0; r < mCPRows; r++ )
610 for (
int c = 0;
c < mCPCols - 1;
c++ )
613 mCPLegalMatrix[r].insert( 1 +
c * 2,
false );
616 mCPCols += mCPCols - 1;
617 for (
int c = 1;
c < mCPCols - 1;
c += 2 )
626 double myDestX, myDestY;
627 destPointOnCPMatrix( row, col, &myDestX, &myDestY );
633 mCPMatrix[row][col] = ct.
transform( myDestPoint );
634 mCPLegalMatrix[row][col] =
true;
638 mCPLegalMatrix[row][col] =
false;
645 mCPLegalMatrix[row][col] =
false;
652 for (
int i = 0; i < mCPCols; i++ )
654 calcCP( row, i, ct );
663 for (
int i = 0; i < mCPRows; i++ )
665 calcCP( i, col, ct );
678 for (
int c = 0;
c < mCPCols;
c++ )
680 for (
int r = 1; r < mCPRows - 1; r += 2 )
682 double myDestX, myDestY;
683 destPointOnCPMatrix( r,
c, &myDestX, &myDestY );
690 QgsPointXY mySrcApprox( ( mySrcPoint1.
x() + mySrcPoint3.
x() ) / 2, ( mySrcPoint1.
y() + mySrcPoint3.
y() ) / 2 );
691 if ( !mCPLegalMatrix[r - 1][
c] || !mCPLegalMatrix[r][
c] || !mCPLegalMatrix[r + 1][
c] )
699 double mySqrDist = myDestApprox.
sqrDist( myDestPoint );
700 if ( mySqrDist > mSqrTolerance )
723 for (
int r = 0; r < mCPRows; r++ )
725 for (
int c = 1;
c < mCPCols - 1;
c += 2 )
727 double myDestX, myDestY;
728 destPointOnCPMatrix( r,
c, &myDestX, &myDestY );
735 QgsPointXY mySrcApprox( ( mySrcPoint1.
x() + mySrcPoint3.
x() ) / 2, ( mySrcPoint1.
y() + mySrcPoint3.
y() ) / 2 );
736 if ( !mCPLegalMatrix[r][
c - 1] || !mCPLegalMatrix[r][
c] || !mCPLegalMatrix[r][
c + 1] )
744 double mySqrDist = myDestApprox.
sqrDist( myDestPoint );
745 if ( mySqrDist > mSqrTolerance )
769 return tr(
"Approximate" );
771 return tr(
"Exact" );
773 return QStringLiteral(
"Unknown" );
779 QgsDebugMsgLevel( QStringLiteral(
"width = %1 height = %2" ).arg( width ).arg( height ), 4 );
789 if ( ! mSrcCRS.
isValid() || ! mDestCRS.
isValid() || mSrcCRS == mDestCRS )
800 ProjectorData pd(
extent, width, height,
mInput, inverseCt, mPrecision, feedback );
805 QgsDebugMsgLevel( QStringLiteral(
"srcExtent:\n%1" ).arg( pd.srcExtent().toString() ), 4 );
806 QgsDebugMsgLevel( QStringLiteral(
"srcCols = %1 srcRows = %2" ).arg( pd.srcCols() ).arg( pd.srcRows() ), 4 );
809 if ( pd.srcRows() <= 0 || pd.srcCols() <= 0 )
815 std::unique_ptr< QgsRasterBlock > inputBlock(
mInput->
block( bandNo, pd.srcExtent(), pd.srcCols(), pd.srcRows(), feedback ) );
816 if ( !inputBlock || inputBlock->isEmpty() )
818 QgsDebugMsg( QStringLiteral(
"No raster data!" ) );
824 std::unique_ptr< QgsRasterBlock > outputBlock(
new QgsRasterBlock( inputBlock->dataType(), width, height ) );
825 if ( inputBlock->hasNoDataValue() )
827 outputBlock->setNoDataValue( inputBlock->noDataValue() );
829 if ( !outputBlock->isValid() )
831 QgsDebugMsg( QStringLiteral(
"Cannot create block" ) );
832 return outputBlock.release();
836 outputBlock->setIsNoData();
850 outputBlock->setIsNoData();
853 for (
int i = 0; i < height; ++i )
857 for (
int j = 0; j < width; ++j )
859 bool inside = pd.srcRowCol( i, j, &srcRow, &srcCol );
860 if ( !inside )
continue;
862 qgssize srcIndex =
static_cast< qgssize >( srcRow * pd.srcCols() + srcCol );
865 if ( doNoData && inputBlock->isNoData( srcRow, srcCol ) )
867 outputBlock->setIsNoData( i, j );
872 char *srcBits = inputBlock->bits( srcIndex );
873 char *destBits = outputBlock->bits( destIndex );
884 memcpy( destBits, srcBits, pixelSize );
885 outputBlock->setIsData( i, j );
889 return outputBlock.release();
893 QgsRectangle &destExtent,
int &destXSize,
int &destYSize )
895 if ( srcExtent.
isEmpty() || srcXSize <= 0 || srcYSize <= 0 )
905 return extentSize( ct, srcExtent, srcXSize, srcYSize, destExtent, destXSize, destYSize );
909 const QgsRectangle &srcExtent,
int srcXSize,
int srcYSize,
910 QgsRectangle &destExtent,
int &destXSize,
int &destYSize )
912 if ( srcExtent.
isEmpty() || srcXSize <= 0 || srcYSize <= 0 )
921 double srcXStep = srcExtent.
width() / 3;
922 double srcYStep = srcExtent.
height() / 3;
923 double srcXRes = srcExtent.
width() / srcXSize;
924 double srcYRes = srcExtent.
height() / srcYSize;
925 double destXRes = std::numeric_limits<double>::max();
926 double destYRes = std::numeric_limits<double>::max();
928 for (
int i = 0; i < 3; i++ )
930 double x = srcExtent.
xMinimum() + i * srcXStep;
931 for (
int j = 0; j < 3; j++ )
933 double y = srcExtent.
yMinimum() + j * srcYStep;
934 QgsRectangle srcRectangle( x - srcXRes / 2, y - srcYRes / 2, x + srcXRes / 2, y + srcYRes / 2 );
938 if ( destRectangle.
width() > 0 )
940 destXRes = std::min( destXRes, destRectangle.
width() );
942 if ( destRectangle.
height() > 0 )
944 destYRes = std::min( destYRes, destRectangle.
height() );
953 destXSize = std::max( 1,
static_cast< int >( destExtent.
width() / destYRes ) );
954 destYSize = std::max( 1,
static_cast< int >( destExtent.
height() / destYRes ) );