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 const double myDestRes = mDestXRes < mDestYRes ? mDestXRes : mDestYRes;
148 mSqrTolerance = myDestRes * myDestRes;
156 mApproximate =
false;
161 mCPCols = mCPRows = 3;
169 if ( std::fabs( -mDestExtent.yMinimum() - mDestExtent.yMaximum() ) / height < 0.5 * mDestYRes )
172 for (
int i = 0; i < mCPRows; i++ )
174 QList<QgsPointXY> myRow;
178 mCPMatrix.insert( i, myRow );
180 QList<bool> myLegalRow;
181 myLegalRow.append(
bool(
false ) );
182 myLegalRow.append(
bool(
false ) );
183 myLegalRow.append(
bool(
false ) );
184 mCPLegalMatrix.insert( i, myLegalRow );
186 for (
int i = 0; i < mCPRows; i++ )
188 calcRow( i, inverseCt );
193 const bool myColsOK = checkCols( inverseCt );
196 insertRows( inverseCt );
198 const bool myRowsOK = checkRows( inverseCt );
201 insertCols( inverseCt );
203 if ( myColsOK && myRowsOK )
210 if ( mCPRows * mCPCols > 0.25 * mDestRows * mDestCols )
214 mApproximate =
false;
222 QgsDebugMsgLevel( QStringLiteral(
"CPMatrix size: mCPRows = %1 mCPCols = %2" ).arg( mCPRows ).arg( mCPCols ), 4 );
223 mDestRowsPerMatrixRow =
static_cast< double >( mDestRows ) / ( mCPRows - 1 );
224 mDestColsPerMatrixCol =
static_cast< double >( mDestCols ) / ( mCPCols - 1 );
234 calcHelper( 0, pHelperTop );
235 calcHelper( 1, pHelperBottom );
241 mSrcYRes = mSrcExtent.height() / mSrcRows;
242 mSrcXRes = mSrcExtent.width() / mSrcCols;
245 ProjectorData::~ProjectorData()
248 delete[] pHelperBottom;
252 void ProjectorData::calcSrcExtent()
263 mSrcExtent =
QgsRectangle( myPoint.
x(), myPoint.
y(), myPoint.
x(), myPoint.
y() );
264 for (
int i = 0; i < mCPRows; i++ )
266 for (
int j = 0; j < mCPCols ; j++ )
268 myPoint = mCPMatrix[i][j];
269 if ( mCPLegalMatrix[i][j] )
271 mSrcExtent.combineExtentWith( myPoint.
x(), myPoint.
y() );
278 mSrcExtent = mSrcExtent.intersect( mExtent );
289 if ( !mExtent.isEmpty() )
291 if ( mMaxSrcXRes > 0 )
294 double col = std::floor( ( mSrcExtent.xMinimum() - mExtent.xMinimum() ) / mMaxSrcXRes );
295 double x = mExtent.xMinimum() + col * mMaxSrcXRes;
296 mSrcExtent.setXMinimum( x );
298 col = std::ceil( ( mSrcExtent.xMaximum() - mExtent.xMinimum() ) / mMaxSrcXRes );
299 x = mExtent.xMinimum() + col * mMaxSrcXRes;
300 mSrcExtent.setXMaximum( x );
302 if ( mMaxSrcYRes > 0 )
304 double row = std::floor( ( mExtent.yMaximum() - mSrcExtent.yMaximum() ) / mMaxSrcYRes );
305 double y = mExtent.yMaximum() - row * mMaxSrcYRes;
306 mSrcExtent.setYMaximum( y );
308 row = std::ceil( ( mExtent.yMaximum() - mSrcExtent.yMinimum() ) / mMaxSrcYRes );
309 y = mExtent.yMaximum() - row * mMaxSrcYRes;
310 mSrcExtent.setYMinimum( y );
316 QString ProjectorData::cpToString()
const
319 for (
int i = 0; i < mCPRows; i++ )
323 for (
int j = 0; j < mCPCols; j++ )
326 myString += QLatin1String(
" " );
328 if ( mCPLegalMatrix[i][j] )
334 myString += QLatin1String(
"(-,-)" );
341 void ProjectorData::calcSrcRowsCols()
349 double myMinSize = std::numeric_limits<double>::max();
353 double myMaxSize = 0;
356 const double myDestColsPerMatrixCell =
static_cast< double >( mDestCols ) / mCPCols;
357 const double myDestRowsPerMatrixCell =
static_cast< double >( mDestRows ) / mCPRows;
358 QgsDebugMsgLevel( QStringLiteral(
"myDestColsPerMatrixCell = %1 myDestRowsPerMatrixCell = %2" ).arg( myDestColsPerMatrixCell ).arg( myDestRowsPerMatrixCell ), 4 );
359 for (
int i = 0; i < mCPRows - 1; i++ )
361 for (
int j = 0; j < mCPCols - 1; j++ )
364 const QgsPointXY myPointB = mCPMatrix[i][j + 1];
365 const QgsPointXY myPointC = mCPMatrix[i + 1][j];
366 if ( mCPLegalMatrix[i][j] && mCPLegalMatrix[i][j + 1] && mCPLegalMatrix[i + 1][j] )
368 double mySize = std::sqrt( myPointA.
sqrDist( myPointB ) ) / myDestColsPerMatrixCell;
369 if ( mySize < myMinSize )
371 if ( mySize > myMaxSize )
374 mySize = std::sqrt( myPointA.
sqrDist( myPointC ) ) / myDestRowsPerMatrixCell;
375 if ( mySize < myMinSize )
377 if ( mySize > myMaxSize )
385 if ( myMinSize < 0.1 * myMaxSize )
386 myMinSize = 0.1 * myMaxSize;
393 int srcXSize, srcYSize;
396 const double srcXRes = srcExtent.
width() / srcXSize;
397 const double srcYRes = srcExtent.
height() / srcYSize;
398 myMinSize = std::min( srcXRes, srcYRes );
402 QgsDebugMsg( QStringLiteral(
"Cannot get src extent/size" ) );
411 QgsDebugMsgLevel( QStringLiteral(
"mMaxSrcXRes = %1 mMaxSrcYRes = %2" ).arg( mMaxSrcXRes ).arg( mMaxSrcYRes ), 4 );
413 const double myMinXSize = mMaxSrcXRes > myMinSize ? mMaxSrcXRes : myMinSize;
414 const double myMinYSize = mMaxSrcYRes > myMinSize ? mMaxSrcYRes : myMinSize;
415 QgsDebugMsgLevel( QStringLiteral(
"myMinXSize = %1 myMinYSize = %2" ).arg( myMinXSize ).arg( myMinYSize ), 4 );
416 QgsDebugMsgLevel( QStringLiteral(
"mSrcExtent.width = %1 mSrcExtent.height = %2" ).arg( mSrcExtent.width() ).arg( mSrcExtent.height() ), 4 );
421 double dblSrcRows = mSrcExtent.height() / myMinYSize;
422 if ( dblSrcRows > mDestRows * 10 )
423 mSrcRows = mDestRows * 10;
425 mSrcRows =
static_cast< int >( std::round( dblSrcRows ) );
427 double dblSrcCols = mSrcExtent.width() / myMinXSize;
428 if ( dblSrcCols > mDestCols * 10 )
429 mSrcCols = mDestCols * 10;
431 mSrcCols =
static_cast< int >( std::round( dblSrcCols ) );
433 QgsDebugMsgLevel( QStringLiteral(
"mSrcRows = %1 mSrcCols = %2" ).arg( mSrcRows ).arg( mSrcCols ), 4 );
437 inline void ProjectorData::destPointOnCPMatrix(
int row,
int col,
double *theX,
double *theY )
439 *theX = mDestExtent.xMinimum() + col * mDestExtent.width() / ( mCPCols - 1 );
440 *theY = mDestExtent.yMaximum() - row * mDestExtent.height() / ( mCPRows - 1 );
443 inline int ProjectorData::matrixRow(
int destRow )
445 return static_cast< int >( std::floor( ( destRow + 0.5 ) / mDestRowsPerMatrixRow ) );
447 inline int ProjectorData::matrixCol(
int destCol )
449 return static_cast< int >( std::floor( ( destCol + 0.5 ) / mDestColsPerMatrixCol ) );
452 void ProjectorData::calcHelper(
int matrixRow,
QgsPointXY *points )
455 for (
int myDestCol = 0; myDestCol < mDestCols; myDestCol++ )
457 const double myDestX = mDestExtent.xMinimum() + ( myDestCol + 0.5 ) * mDestXRes;
459 const int myMatrixCol = matrixCol( myDestCol );
461 double myDestXMin, myDestYMin, myDestXMax, myDestYMax;
463 destPointOnCPMatrix( matrixRow, myMatrixCol, &myDestXMin, &myDestYMin );
464 destPointOnCPMatrix( matrixRow, myMatrixCol + 1, &myDestXMax, &myDestYMax );
466 const double xfrac = ( myDestX - myDestXMin ) / ( myDestXMax - myDestXMin );
468 const QgsPointXY &mySrcPoint0 = mCPMatrix[matrixRow][myMatrixCol];
469 const QgsPointXY &mySrcPoint1 = mCPMatrix[matrixRow][myMatrixCol + 1];
470 const double s = mySrcPoint0.
x() + ( mySrcPoint1.
x() - mySrcPoint0.
x() ) * xfrac;
471 const double t = mySrcPoint0.
y() + ( mySrcPoint1.
y() - mySrcPoint0.
y() ) * xfrac;
473 points[myDestCol].
setX( s );
474 points[myDestCol].
setY( t );
478 void ProjectorData::nextHelper()
483 pHelperTop = pHelperBottom;
485 calcHelper( mHelperTopRow + 2, pHelperBottom );
489 bool ProjectorData::srcRowCol(
int destRow,
int destCol,
int *srcRow,
int *srcCol )
493 return approximateSrcRowCol( destRow, destCol, srcRow, srcCol );
497 return preciseSrcRowCol( destRow, destCol, srcRow, srcCol );
501 bool ProjectorData::preciseSrcRowCol(
int destRow,
int destCol,
int *srcRow,
int *srcCol )
503 #if 0 // too slow, even if we only run it on debug builds!
505 QgsDebugMsgLevel( QStringLiteral(
"theDestRow = %1 mDestExtent.yMaximum() = %2 mDestYRes = %3" ).arg( destRow ).arg( mDestExtent.yMaximum() ).arg( mDestYRes ), 5 );
509 double x = mDestExtent.xMinimum() + ( destCol + 0.5 ) * mDestXRes;
510 double y = mDestExtent.yMaximum() - ( destRow + 0.5 ) * mDestYRes;
517 if ( mInverseCt.isValid() )
521 mInverseCt.transformInPlace( x, y, z );
533 if ( !mExtent.contains( x, y ) )
538 *srcRow =
static_cast< int >( std::floor( ( mSrcExtent.yMaximum() - y ) / mSrcYRes ) );
539 *srcCol =
static_cast< int >( std::floor( ( x - mSrcExtent.xMinimum() ) / mSrcXRes ) );
541 QgsDebugMsgLevel( QStringLiteral(
"mSrcExtent.yMinimum() = %1 mSrcExtent.yMaximum() = %2 mSrcYRes = %3" ).arg( mSrcExtent.yMinimum() ).arg( mSrcExtent.yMaximum() ).arg( mSrcYRes ), 5 );
542 QgsDebugMsgLevel( QStringLiteral(
"theSrcRow = %1 srcCol = %2" ).arg( *srcRow ).arg( *srcCol ), 5 );
549 if ( *srcRow >= mSrcRows )
return false;
550 if ( *srcRow < 0 )
return false;
551 if ( *srcCol >= mSrcCols )
return false;
552 if ( *srcCol < 0 )
return false;
557 bool ProjectorData::approximateSrcRowCol(
int destRow,
int destCol,
int *srcRow,
int *srcCol )
559 const int myMatrixRow = matrixRow( destRow );
560 const int myMatrixCol = matrixCol( destCol );
562 if ( myMatrixRow > mHelperTopRow )
568 const double myDestY = mDestExtent.yMaximum() - ( destRow + 0.5 ) * mDestYRes;
572 double myDestXMin, myDestYMin, myDestXMax, myDestYMax;
574 destPointOnCPMatrix( myMatrixRow + 1, myMatrixCol, &myDestXMin, &myDestYMin );
575 destPointOnCPMatrix( myMatrixRow, myMatrixCol + 1, &myDestXMax, &myDestYMax );
577 const double yfrac = ( myDestY - myDestYMin ) / ( myDestYMax - myDestYMin );
579 const QgsPointXY &myTop = pHelperTop[destCol];
580 const QgsPointXY &myBot = pHelperBottom[destCol];
586 const double tx = myTop.
x();
587 const double ty = myTop.
y();
588 const double bx = myBot.
x();
589 const double by = myBot.
y();
590 const double mySrcX = bx + ( tx - bx ) * yfrac;
591 const double mySrcY = by + ( ty - by ) * yfrac;
593 if ( !mExtent.contains( mySrcX, mySrcY ) )
600 *srcRow =
static_cast< int >( std::floor( ( mSrcExtent.yMaximum() - mySrcY ) / mSrcYRes ) );
601 *srcCol =
static_cast< int >( std::floor( ( mySrcX - mSrcExtent.xMinimum() ) / mSrcXRes ) );
606 if ( *srcRow >= mSrcRows )
return false;
607 if ( *srcRow < 0 )
return false;
608 if ( *srcCol >= mSrcCols )
return false;
609 if ( *srcCol < 0 )
return false;
616 for (
int r = 0; r < mCPRows - 1; r++ )
618 QList<QgsPointXY> myRow;
619 QList<bool> myLegalRow;
620 myRow.reserve( mCPCols );
621 myLegalRow.reserve( mCPCols );
622 for (
int c = 0;
c < mCPCols; ++
c )
625 myLegalRow.append(
false );
627 QgsDebugMsgLevel( QStringLiteral(
"insert new row at %1" ).arg( 1 + r * 2 ), 3 );
628 mCPMatrix.insert( 1 + r * 2, myRow );
629 mCPLegalMatrix.insert( 1 + r * 2, myLegalRow );
631 mCPRows += mCPRows - 1;
632 for (
int r = 1; r < mCPRows - 1; r += 2 )
640 for (
int r = 0; r < mCPRows; r++ )
642 for (
int c = 0;
c < mCPCols - 1;
c++ )
645 mCPLegalMatrix[r].insert( 1 +
c * 2,
false );
648 mCPCols += mCPCols - 1;
649 for (
int c = 1;
c < mCPCols - 1;
c += 2 )
658 double myDestX, myDestY;
659 destPointOnCPMatrix( row, col, &myDestX, &myDestY );
660 const QgsPointXY myDestPoint( myDestX, myDestY );
665 mCPMatrix[row][col] = ct.
transform( myDestPoint );
666 mCPLegalMatrix[row][col] =
true;
670 mCPLegalMatrix[row][col] =
false;
677 mCPLegalMatrix[row][col] =
false;
684 for (
int i = 0; i < mCPCols; i++ )
686 calcCP( row, i, ct );
695 for (
int i = 0; i < mCPRows; i++ )
697 calcCP( i, col, ct );
710 for (
int c = 0;
c < mCPCols;
c++ )
712 for (
int r = 1; r < mCPRows - 1; r += 2 )
714 double myDestX, myDestY;
715 destPointOnCPMatrix( r,
c, &myDestX, &myDestY );
716 const QgsPointXY myDestPoint( myDestX, myDestY );
718 const QgsPointXY mySrcPoint1 = mCPMatrix[r - 1][
c];
720 const QgsPointXY mySrcPoint3 = mCPMatrix[r + 1][
c];
722 const QgsPointXY mySrcApprox( ( mySrcPoint1.
x() + mySrcPoint3.
x() ) / 2, ( mySrcPoint1.
y() + mySrcPoint3.
y() ) / 2 );
723 if ( !mCPLegalMatrix[r - 1][
c] || !mCPLegalMatrix[r][
c] || !mCPLegalMatrix[r + 1][
c] )
730 const QgsPointXY myDestApprox = ct.
transform( mySrcApprox, Qgis::TransformDirection::Reverse );
731 const double mySqrDist = myDestApprox.
sqrDist( myDestPoint );
732 if ( mySqrDist > mSqrTolerance )
755 for (
int r = 0; r < mCPRows; r++ )
757 for (
int c = 1;
c < mCPCols - 1;
c += 2 )
759 double myDestX, myDestY;
760 destPointOnCPMatrix( r,
c, &myDestX, &myDestY );
762 const QgsPointXY myDestPoint( myDestX, myDestY );
763 const QgsPointXY mySrcPoint1 = mCPMatrix[r][
c - 1];
765 const QgsPointXY mySrcPoint3 = mCPMatrix[r][
c + 1];
767 const QgsPointXY mySrcApprox( ( mySrcPoint1.
x() + mySrcPoint3.
x() ) / 2, ( mySrcPoint1.
y() + mySrcPoint3.
y() ) / 2 );
768 if ( !mCPLegalMatrix[r][
c - 1] || !mCPLegalMatrix[r][
c] || !mCPLegalMatrix[r][
c + 1] )
775 const QgsPointXY myDestApprox = ct.
transform( mySrcApprox, Qgis::TransformDirection::Reverse );
776 const double mySqrDist = myDestApprox.
sqrDist( myDestPoint );
777 if ( mySqrDist > mSqrTolerance )
801 return tr(
"Approximate" );
803 return tr(
"Exact" );
805 return QStringLiteral(
"Unknown" );
811 QgsDebugMsgLevel( QStringLiteral(
"width = %1 height = %2" ).arg( width ).arg( height ), 4 );
821 if ( ! mSrcCRS.
isValid() || ! mDestCRS.
isValid() || mSrcCRS == mDestCRS )
832 ProjectorData pd(
extent, width, height,
mInput, inverseCt, mPrecision, feedback );
837 QgsDebugMsgLevel( QStringLiteral(
"srcExtent:\n%1" ).arg( pd.srcExtent().toString() ), 4 );
838 QgsDebugMsgLevel( QStringLiteral(
"srcCols = %1 srcRows = %2" ).arg( pd.srcCols() ).arg( pd.srcRows() ), 4 );
841 if ( pd.srcRows() <= 0 || pd.srcCols() <= 0 )
847 std::unique_ptr< QgsRasterBlock > inputBlock(
mInput->
block( bandNo, pd.srcExtent(), pd.srcCols(), pd.srcRows(), feedback ) );
848 if ( !inputBlock || inputBlock->isEmpty() )
850 QgsDebugMsg( QStringLiteral(
"No raster data!" ) );
856 std::unique_ptr< QgsRasterBlock > outputBlock(
new QgsRasterBlock( inputBlock->dataType(), width, height ) );
857 if ( inputBlock->hasNoDataValue() )
859 outputBlock->setNoDataValue( inputBlock->noDataValue() );
861 if ( !outputBlock->isValid() )
863 QgsDebugMsg( QStringLiteral(
"Cannot create block" ) );
864 return outputBlock.release();
868 outputBlock->setIsNoData();
882 outputBlock->setIsNoData();
885 for (
int i = 0; i < height; ++i )
889 for (
int j = 0; j < width; ++j )
891 const bool inside = pd.srcRowCol( i, j, &srcRow, &srcCol );
892 if ( !inside )
continue;
894 const qgssize srcIndex =
static_cast< qgssize >( srcRow ) * pd.srcCols() + srcCol;
897 if ( doNoData && inputBlock->isNoData( srcRow, srcCol ) )
899 outputBlock->setIsNoData( i, j );
903 const qgssize destIndex =
static_cast< qgssize >( i ) * width + j;
904 char *srcBits = inputBlock->bits( srcIndex );
905 char *destBits = outputBlock->bits( destIndex );
916 memcpy( destBits, srcBits, pixelSize );
917 outputBlock->setIsData( i, j );
921 return outputBlock.release();
925 QgsRectangle &destExtent,
int &destXSize,
int &destYSize )
927 if ( srcExtent.
isEmpty() || srcXSize <= 0 || srcYSize <= 0 )
937 return extentSize( ct, srcExtent, srcXSize, srcYSize, destExtent, destXSize, destYSize );
941 const QgsRectangle &srcExtent,
int srcXSize,
int srcYSize,
942 QgsRectangle &destExtent,
int &destXSize,
int &destYSize )
944 if ( srcExtent.
isEmpty() || srcXSize <= 0 || srcYSize <= 0 )
956 constexpr
int steps = 3;
957 const double srcXStep = srcExtent.
width() / steps;
958 const double srcYStep = srcExtent.
height() / steps;
959 const double srcXRes = srcExtent.
width() / srcXSize;
960 const double srcYRes = srcExtent.
height() / srcYSize;
961 double destXRes = std::numeric_limits<double>::max();
962 double destYRes = std::numeric_limits<double>::max();
966 for (
int i = 0; i < steps; i++ )
968 const double x = srcExtent.
xMinimum() + i * srcXStep;
969 for (
int j = 0; j < steps; j++ )
971 const double y = srcExtent.
yMinimum() + j * srcYStep;
972 const QgsRectangle srcRectangle( x - srcXRes / 2, y - srcYRes / 2, x + srcXRes / 2, y + srcYRes / 2 );
976 if ( destRectangle.
width() > 0 )
978 destXRes = std::min( destXRes, destRectangle.
width() );
979 if ( destRectangle.
width() > maxXRes )
980 maxXRes = destRectangle.
width();
982 if ( destRectangle.
height() > 0 )
984 destYRes = std::min( destYRes, destRectangle.
height() );
985 if ( destRectangle.
height() > maxYRes )
986 maxYRes = destRectangle.
height();
999 if ( destXRes < 0.1 * maxXRes )
1001 destXRes = 0.1 * maxXRes;
1003 if ( destYRes < 0.1 * maxYRes )
1005 destYRes = 0.1 * maxYRes;
1007 if ( destXRes == 0 || destExtent.
width() / destXRes > std::numeric_limits<int>::max() )
1009 if ( destYRes == 0 || destExtent.
height() / destYRes > std::numeric_limits<int>::max() )
1012 destXSize = std::max( 1,
static_cast< int >( destExtent.
width() / destXRes ) );
1013 destYSize = std::max( 1,
static_cast< int >( destExtent.
height() / destYRes ) );