QGIS API Documentation  2.12.0-Lyon
qgsrasterprojector.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrasterprojector.cpp - Raster projector
3  --------------------------------------
4  Date : Jan 16, 2011
5  Copyright : (C) 2005 by Radim Blazek
6  email : radim dot blazek at gmail dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 #include <algorithm>
18 
19 #include "qgsrasterdataprovider.h"
20 #include "qgscrscache.h"
21 #include "qgslogger.h"
22 #include "qgsrasterprojector.h"
23 #include "qgscoordinatetransform.h"
24 
26  const QgsCoordinateReferenceSystem& theSrcCRS,
27  const QgsCoordinateReferenceSystem& theDestCRS,
28  int theSrcDatumTransform,
29  int theDestDatumTransform,
30  const QgsRectangle& theDestExtent,
31  int theDestRows, int theDestCols,
32  double theMaxSrcXRes, double theMaxSrcYRes,
33  const QgsRectangle& theExtent )
34  : QgsRasterInterface( 0 )
35  , mSrcCRS( theSrcCRS )
36  , mDestCRS( theDestCRS )
37  , mSrcDatumTransform( theSrcDatumTransform )
38  , mDestDatumTransform( theDestDatumTransform )
39  , mDestExtent( theDestExtent )
40  , mExtent( theExtent )
41  , mDestRows( theDestRows ), mDestCols( theDestCols )
42  , pHelperTop( 0 ), pHelperBottom( 0 )
43  , mMaxSrcXRes( theMaxSrcXRes ), mMaxSrcYRes( theMaxSrcYRes )
44  , mPrecision( Approximate )
45  , mApproximate( true )
46 {
47  QgsDebugMsg( "Entered" );
48  QgsDebugMsg( "theDestExtent = " + theDestExtent.toString() );
49 
50  calc();
51 }
52 
54  const QgsCoordinateReferenceSystem& theSrcCRS,
55  const QgsCoordinateReferenceSystem& theDestCRS,
56  const QgsRectangle& theDestExtent,
57  int theDestRows, int theDestCols,
58  double theMaxSrcXRes, double theMaxSrcYRes,
59  const QgsRectangle& theExtent )
60  : QgsRasterInterface( 0 )
61  , mSrcCRS( theSrcCRS )
62  , mDestCRS( theDestCRS )
63  , mSrcDatumTransform( -1 )
64  , mDestDatumTransform( -1 )
65  , mDestExtent( theDestExtent )
66  , mExtent( theExtent )
67  , mDestRows( theDestRows ), mDestCols( theDestCols )
68  , pHelperTop( 0 ), pHelperBottom( 0 )
69  , mMaxSrcXRes( theMaxSrcXRes ), mMaxSrcYRes( theMaxSrcYRes )
70  , mPrecision( Approximate )
71  , mApproximate( false )
72 {
73  QgsDebugMsg( "Entered" );
74  QgsDebugMsg( "theDestExtent = " + theDestExtent.toString() );
75 
76  calc();
77 }
78 
80  const QgsCoordinateReferenceSystem& theSrcCRS,
81  const QgsCoordinateReferenceSystem& theDestCRS,
82  double theMaxSrcXRes, double theMaxSrcYRes,
83  const QgsRectangle& theExtent )
84  : QgsRasterInterface( 0 )
85  , mSrcCRS( theSrcCRS )
86  , mDestCRS( theDestCRS )
87  , mSrcDatumTransform( -1 )
88  , mDestDatumTransform( -1 )
89  , mExtent( theExtent )
90  , mDestRows( 0 )
91  , mDestCols( 0 )
92  , mDestXRes( 0.0 )
93  , mDestYRes( 0.0 )
94  , mSrcRows( 0 )
95  , mSrcCols( 0 )
96  , mSrcXRes( 0.0 )
97  , mSrcYRes( 0.0 )
98  , mDestRowsPerMatrixRow( 0.0 )
99  , mDestColsPerMatrixCol( 0.0 )
100  , pHelperTop( 0 ), pHelperBottom( 0 )
101  , mHelperTopRow( 0 )
102  , mCPCols( 0 )
103  , mCPRows( 0 )
104  , mSqrTolerance( 0.0 )
105  , mMaxSrcXRes( theMaxSrcXRes )
106  , mMaxSrcYRes( theMaxSrcYRes )
107  , mPrecision( Approximate )
108  , mApproximate( false )
109 {
110  QgsDebugMsg( "Entered" );
111 }
112 
114  : QgsRasterInterface( 0 )
115  , mSrcDatumTransform( -1 )
116  , mDestDatumTransform( -1 )
117  , mDestRows( 0 )
118  , mDestCols( 0 )
119  , mDestXRes( 0.0 )
120  , mDestYRes( 0.0 )
121  , mSrcRows( 0 )
122  , mSrcCols( 0 )
123  , mSrcXRes( 0.0 )
124  , mSrcYRes( 0.0 )
125  , mDestRowsPerMatrixRow( 0.0 )
126  , mDestColsPerMatrixCol( 0.0 )
127  , pHelperTop( 0 )
128  , pHelperBottom( 0 )
129  , mHelperTopRow( 0 )
130  , mCPCols( 0 )
131  , mCPRows( 0 )
132  , mSqrTolerance( 0.0 )
133  , mMaxSrcXRes( 0 )
134  , mMaxSrcYRes( 0 )
135  , mPrecision( Approximate )
136  , mApproximate( false )
137 {
138  QgsDebugMsg( "Entered" );
139 }
140 
142  : QgsRasterInterface( 0 )
143  , pHelperTop( NULL )
144  , pHelperBottom( NULL )
145  , mHelperTopRow( 0 )
146  , mCPCols( 0 )
147  , mCPRows( 0 )
148  , mSqrTolerance( 0 )
149  , mApproximate( true )
150 {
151  mSrcCRS = projector.mSrcCRS;
152  mDestCRS = projector.mDestCRS;
153  mSrcDatumTransform = projector.mSrcDatumTransform;
154  mDestDatumTransform = projector.mDestDatumTransform;
155  mMaxSrcXRes = projector.mMaxSrcXRes;
156  mMaxSrcYRes = projector.mMaxSrcYRes;
157  mExtent = projector.mExtent;
158  mDestRows = projector.mDestRows;
159  mDestCols = projector.mDestCols;
160  mDestXRes = projector.mDestXRes;
161  mDestYRes = projector.mDestYRes;
162  mSrcRows = projector.mSrcRows;
163  mSrcCols = projector.mSrcCols;
164  mSrcXRes = projector.mSrcXRes;
165  mSrcYRes = projector.mSrcYRes;
166  mDestRowsPerMatrixRow = projector.mDestRowsPerMatrixRow;
167  mDestColsPerMatrixCol = projector.mDestColsPerMatrixCol;
168  mPrecision = projector.mPrecision;
169 }
170 
172 {
173  if ( &projector != this )
174  {
175  mSrcCRS = projector.mSrcCRS;
176  mDestCRS = projector.mDestCRS;
177  mSrcDatumTransform = projector.mSrcDatumTransform;
178  mDestDatumTransform = projector.mDestDatumTransform;
179  mMaxSrcXRes = projector.mMaxSrcXRes;
180  mMaxSrcYRes = projector.mMaxSrcYRes;
181  mExtent = projector.mExtent;
182  mPrecision = projector.mPrecision;
183  }
184  return *this;
185 }
186 
188 {
189  QgsDebugMsg( "Entered" );
190  QgsRasterProjector * projector = new QgsRasterProjector( mSrcCRS, mDestCRS, mMaxSrcXRes, mMaxSrcYRes, mExtent );
191  projector->mSrcDatumTransform = mSrcDatumTransform;
192  projector->mDestDatumTransform = mDestDatumTransform;
193  projector->mPrecision = mPrecision;
194  return projector;
195 }
196 
198 {
199  delete[] pHelperTop;
200  delete[] pHelperBottom;
201 }
202 
204 {
205  if ( mInput ) return mInput->bandCount();
206 
207  return 0;
208 }
209 
211 {
212  if ( mInput ) return mInput->dataType( bandNo );
213 
214  return QGis::UnknownDataType;
215 }
216 
217 void QgsRasterProjector::setCRS( const QgsCoordinateReferenceSystem & theSrcCRS, const QgsCoordinateReferenceSystem & theDestCRS, int srcDatumTransform, int destDatumTransform )
218 {
219  mSrcCRS = theSrcCRS;
220  mDestCRS = theDestCRS;
221  mSrcDatumTransform = srcDatumTransform;
222  mDestDatumTransform = destDatumTransform;
223 }
224 
225 void QgsRasterProjector::calc()
226 {
227  QgsDebugMsg( "Entered" );
228  mCPMatrix.clear();
229  mCPLegalMatrix.clear();
230  delete[] pHelperTop;
231  pHelperTop = 0;
232  delete[] pHelperBottom;
233  pHelperBottom = 0;
234 
235  // Get max source resolution and extent if possible
236  mMaxSrcXRes = 0;
237  mMaxSrcYRes = 0;
238  if ( mInput )
239  {
240  QgsRasterDataProvider *provider = dynamic_cast<QgsRasterDataProvider*>( mInput->srcInput() );
241  if ( provider )
242  {
243  if ( provider->capabilities() & QgsRasterDataProvider::Size )
244  {
245  mMaxSrcXRes = provider->extent().width() / provider->xSize();
246  mMaxSrcYRes = provider->extent().height() / provider->ySize();
247  }
248  // Get source extent
249  if ( mExtent.isEmpty() )
250  {
251  mExtent = provider->extent();
252  }
253  }
254  }
255 
256  mDestXRes = mDestExtent.width() / ( mDestCols );
257  mDestYRes = mDestExtent.height() / ( mDestRows );
258 
259  // Calculate tolerance
260  // TODO: Think it over better
261  // Note: we are checking on matrix each even point, that means that the real error
262  // in that moment is approximately half size
263  double myDestRes = mDestXRes < mDestYRes ? mDestXRes : mDestYRes;
264  mSqrTolerance = myDestRes * myDestRes;
265 
266  const QgsCoordinateTransform* inverseCt = QgsCoordinateTransformCache::instance()->transform( mDestCRS.authid(), mSrcCRS.authid(), mDestDatumTransform, mSrcDatumTransform );
267 
268  if ( mPrecision == Approximate )
269  {
270  // Initialize the matrix by corners and middle points
271  mCPCols = mCPRows = 3;
272  mApproximate = true;
273  for ( int i = 0; i < mCPRows; i++ )
274  {
275  QList<QgsPoint> myRow;
276  myRow.append( QgsPoint() );
277  myRow.append( QgsPoint() );
278  myRow.append( QgsPoint() );
279  mCPMatrix.insert( i, myRow );
280  // And the legal points
281  QList<bool> myLegalRow;
282  myLegalRow.append( bool( false ) );
283  myLegalRow.append( bool( false ) );
284  myLegalRow.append( bool( false ) );
285  mCPLegalMatrix.insert( i, myLegalRow );
286  }
287  for ( int i = 0; i < mCPRows; i++ )
288  {
289  calcRow( i, inverseCt );
290  }
291 
292  while ( true )
293  {
294  bool myColsOK = checkCols( inverseCt );
295  if ( !myColsOK )
296  {
297  insertRows( inverseCt );
298  }
299  bool myRowsOK = checkRows( inverseCt );
300  if ( !myRowsOK )
301  {
302  insertCols( inverseCt );
303  }
304  if ( myColsOK && myRowsOK )
305  {
306  QgsDebugMsg( "CP matrix within tolerance" );
307  break;
308  }
309  // What is the maximum reasonable size of transformatio matrix?
310  // TODO: consider better when to break - ratio
311  if ( mCPRows * mCPCols > 0.25 * mDestRows * mDestCols )
312  {
313  QgsDebugMsg( "Too large CP matrix" );
314  mApproximate = false;
315  break;
316  }
317  }
318  QgsDebugMsg( QString( "CPMatrix size: mCPRows = %1 mCPCols = %2" ).arg( mCPRows ).arg( mCPCols ) );
319  mDestRowsPerMatrixRow = ( float )mDestRows / ( mCPRows - 1 );
320  mDestColsPerMatrixCol = ( float )mDestCols / ( mCPCols - 1 );
321 
322  QgsDebugMsgLevel( "CPMatrix:", 5 );
323  QgsDebugMsgLevel( cpToString(), 5 );
324 
325  // init helper points
326  pHelperTop = new QgsPoint[mDestCols];
327  pHelperBottom = new QgsPoint[mDestCols];
328  calcHelper( 0, pHelperTop );
329  calcHelper( 1, pHelperBottom );
330  mHelperTopRow = 0;
331  }
332  else
333  {
334  mApproximate = false;
335  }
336  // Calculate source dimensions
337  calcSrcExtent();
338  calcSrcRowsCols();
339  mSrcYRes = mSrcExtent.height() / mSrcRows;
340  mSrcXRes = mSrcExtent.width() / mSrcCols;
341 }
342 
343 void QgsRasterProjector::calcSrcExtent()
344 {
345  /* Run around the mCPMatrix and find source extent */
346  // Attention, source limits are not necessarily on destination edges, e.g.
347  // for destination EPSG:32661 Polar Stereographic and source EPSG:4326,
348  // the maximum y may be in the middle of destination extent
349  // TODO: How to find extent exactly and quickly?
350  // For now, we runt through all matrix
351  if ( mApproximate )
352  {
353  QgsPoint myPoint = mCPMatrix[0][0];
354  mSrcExtent = QgsRectangle( myPoint.x(), myPoint.y(), myPoint.x(), myPoint.y() );
355  for ( int i = 0; i < mCPRows; i++ )
356  {
357  for ( int j = 0; j < mCPCols ; j++ )
358  {
359  myPoint = mCPMatrix[i][j];
360  if ( mCPLegalMatrix[i][j] )
361  {
362  mSrcExtent.combineExtentWith( myPoint.x(), myPoint.y() );
363  }
364  }
365  }
366  // Expand a bit to avoid possible approx coords falling out because of representation error?
367  }
368  else
369  {
370  const QgsCoordinateTransform* inverseCt = QgsCoordinateTransformCache::instance()->transform( mDestCRS.authid(), mSrcCRS.authid(), mDestDatumTransform, mSrcDatumTransform );
371  mSrcExtent = inverseCt->transformBoundingBox( mDestExtent );
372  }
373 
374  // Combine with maximum source extent
375  mSrcExtent = mSrcExtent.intersect( &mExtent );
376 
377  // If mMaxSrcXRes, mMaxSrcYRes are defined (fixed src resolution)
378  // align extent to src resolution to avoid jumping of reprojected pixels
379  // when shifting resampled grid.
380  // Important especially if we are over mMaxSrcXRes, mMaxSrcYRes limits
381  // Note however, that preceding filters (like resampler) may read data
382  // on different resolution.
383 
384  QgsDebugMsg( "mSrcExtent = " + mSrcExtent.toString() );
385  QgsDebugMsg( "mExtent = " + mExtent.toString() );
386  if ( !mExtent.isEmpty() )
387  {
388  if ( mMaxSrcXRes > 0 )
389  {
390  // with floor/ceil it should work correctly also for mSrcExtent.xMinimum() < mExtent.xMinimum()
391  double col = floor(( mSrcExtent.xMinimum() - mExtent.xMinimum() ) / mMaxSrcXRes );
392  double x = mExtent.xMinimum() + col * mMaxSrcXRes;
393  mSrcExtent.setXMinimum( x );
394 
395  col = ceil(( mSrcExtent.xMaximum() - mExtent.xMinimum() ) / mMaxSrcXRes );
396  x = mExtent.xMinimum() + col * mMaxSrcXRes;
397  mSrcExtent.setXMaximum( x );
398  }
399  if ( mMaxSrcYRes > 0 )
400  {
401  double row = floor(( mExtent.yMaximum() - mSrcExtent.yMaximum() ) / mMaxSrcYRes );
402  double y = mExtent.yMaximum() - row * mMaxSrcYRes;
403  mSrcExtent.setYMaximum( y );
404 
405  row = ceil(( mExtent.yMaximum() - mSrcExtent.yMinimum() ) / mMaxSrcYRes );
406  y = mExtent.yMaximum() - row * mMaxSrcYRes;
407  mSrcExtent.setYMinimum( y );
408  }
409  }
410  QgsDebugMsg( "mSrcExtent = " + mSrcExtent.toString() );
411 }
412 
413 QString QgsRasterProjector::cpToString()
414 {
415  QString myString;
416  for ( int i = 0; i < mCPRows; i++ )
417  {
418  if ( i > 0 )
419  myString += "\n";
420  for ( int j = 0; j < mCPCols; j++ )
421  {
422  if ( j > 0 )
423  myString += " ";
424  QgsPoint myPoint = mCPMatrix[i][j];
425  if ( mCPLegalMatrix[i][j] )
426  {
427  myString += myPoint.toString();
428  }
429  else
430  {
431  myString += "(-,-)";
432  }
433  }
434  }
435  return myString;
436 }
437 
438 void QgsRasterProjector::calcSrcRowsCols()
439 {
440  // Wee need to calculate minimum cell size in the source
441  // TODO: Think it over better, what is the right source resolution?
442  // Taking distances between cell centers projected to source along source
443  // axis would result in very high resolution
444  // TODO: different resolution for rows and cols ?
445 
446  double myMinSize = std::numeric_limits<double>::max();
447 
448  if ( mApproximate )
449  {
450  // For now, we take cell sizes projected to source but not to source axes
451  double myDestColsPerMatrixCell = ( double )mDestCols / mCPCols;
452  double myDestRowsPerMatrixCell = ( double )mDestRows / mCPRows;
453  QgsDebugMsg( QString( "myDestColsPerMatrixCell = %1 myDestRowsPerMatrixCell = %2" ).arg( myDestColsPerMatrixCell ).arg( myDestRowsPerMatrixCell ) );
454  for ( int i = 0; i < mCPRows - 1; i++ )
455  {
456  for ( int j = 0; j < mCPCols - 1; j++ )
457  {
458  QgsPoint myPointA = mCPMatrix[i][j];
459  QgsPoint myPointB = mCPMatrix[i][j+1];
460  QgsPoint myPointC = mCPMatrix[i+1][j];
461  if ( mCPLegalMatrix[i][j] && mCPLegalMatrix[i][j+1] && mCPLegalMatrix[i+1][j] )
462  {
463  double mySize = sqrt( myPointA.sqrDist( myPointB ) ) / myDestColsPerMatrixCell;
464  if ( mySize < myMinSize )
465  myMinSize = mySize;
466 
467  mySize = sqrt( myPointA.sqrDist( myPointC ) ) / myDestRowsPerMatrixCell;
468  if ( mySize < myMinSize )
469  myMinSize = mySize;
470  }
471  }
472  }
473  }
474  else
475  {
476  // take highest from corners, points in in the middle of corners and center (3 x 3 )
477  const QgsCoordinateTransform* inverseCt = QgsCoordinateTransformCache::instance()->transform( mDestCRS.authid(), mSrcCRS.authid(), mDestDatumTransform, mSrcDatumTransform );
478  //double
479  QgsRectangle srcExtent;
480  int srcXSize, srcYSize;
481  if ( extentSize( inverseCt, mDestExtent, mDestCols, mDestRows, srcExtent, srcXSize, srcYSize ) )
482  {
483  double srcXRes = srcExtent.width() / srcXSize;
484  double srcYRes = srcExtent.height() / srcYSize;
485  myMinSize = std::min( srcXRes, srcYRes );
486  }
487  else
488  {
489  QgsDebugMsg( "Cannot get src extent/size" );
490  }
491  }
492 
493  // Make it a bit higher resolution
494  // TODO: find the best coefficient, attention, increasing resolution for WMS
495  // is changing WMS content
496  myMinSize *= 0.75;
497 
498  QgsDebugMsg( QString( "mMaxSrcXRes = %1 mMaxSrcYRes = %2" ).arg( mMaxSrcXRes ).arg( mMaxSrcYRes ) );
499  // mMaxSrcXRes, mMaxSrcYRes may be 0 - no limit (WMS)
500  double myMinXSize = mMaxSrcXRes > myMinSize ? mMaxSrcXRes : myMinSize;
501  double myMinYSize = mMaxSrcYRes > myMinSize ? mMaxSrcYRes : myMinSize;
502  QgsDebugMsg( QString( "myMinXSize = %1 myMinYSize = %2" ).arg( myMinXSize ).arg( myMinYSize ) );
503  QgsDebugMsg( QString( "mSrcExtent.width = %1 mSrcExtent.height = %2" ).arg( mSrcExtent.width() ).arg( mSrcExtent.height() ) );
504 
505  // we have to round to keep alignment set in calcSrcExtent
506  mSrcRows = ( int ) qRound( mSrcExtent.height() / myMinYSize );
507  mSrcCols = ( int ) qRound( mSrcExtent.width() / myMinXSize );
508 
509  QgsDebugMsg( QString( "mSrcRows = %1 mSrcCols = %2" ).arg( mSrcRows ).arg( mSrcCols ) );
510 }
511 
512 
513 inline void QgsRasterProjector::destPointOnCPMatrix( int theRow, int theCol, double *theX, double *theY )
514 {
515  *theX = mDestExtent.xMinimum() + theCol * mDestExtent.width() / ( mCPCols - 1 );
516  *theY = mDestExtent.yMaximum() - theRow * mDestExtent.height() / ( mCPRows - 1 );
517 }
518 
519 inline int QgsRasterProjector::matrixRow( int theDestRow )
520 {
521  return ( int )( floor(( theDestRow + 0.5 ) / mDestRowsPerMatrixRow ) );
522 }
523 inline int QgsRasterProjector::matrixCol( int theDestCol )
524 {
525  return ( int )( floor(( theDestCol + 0.5 ) / mDestColsPerMatrixCol ) );
526 }
527 
528 QgsPoint QgsRasterProjector::srcPoint( int theDestRow, int theCol )
529 {
530  Q_UNUSED( theDestRow );
531  Q_UNUSED( theCol );
532  return QgsPoint();
533 }
534 
535 void QgsRasterProjector::calcHelper( int theMatrixRow, QgsPoint *thePoints )
536 {
537  // TODO?: should we also precalc dest cell center coordinates for x and y?
538  for ( int myDestCol = 0; myDestCol < mDestCols; myDestCol++ )
539  {
540  double myDestX = mDestExtent.xMinimum() + ( myDestCol + 0.5 ) * mDestXRes;
541 
542  int myMatrixCol = matrixCol( myDestCol );
543 
544  double myDestXMin, myDestYMin, myDestXMax, myDestYMax;
545 
546  destPointOnCPMatrix( theMatrixRow, myMatrixCol, &myDestXMin, &myDestYMin );
547  destPointOnCPMatrix( theMatrixRow, myMatrixCol + 1, &myDestXMax, &myDestYMax );
548 
549  double xfrac = ( myDestX - myDestXMin ) / ( myDestXMax - myDestXMin );
550 
551  QgsPoint &mySrcPoint0 = mCPMatrix[theMatrixRow][myMatrixCol];
552  QgsPoint &mySrcPoint1 = mCPMatrix[theMatrixRow][myMatrixCol+1];
553  double s = mySrcPoint0.x() + ( mySrcPoint1.x() - mySrcPoint0.x() ) * xfrac;
554  double t = mySrcPoint0.y() + ( mySrcPoint1.y() - mySrcPoint0.y() ) * xfrac;
555 
556  thePoints[myDestCol].setX( s );
557  thePoints[myDestCol].setY( t );
558  }
559 }
560 void QgsRasterProjector::nextHelper()
561 {
562  // We just switch pHelperTop and pHelperBottom, memory is not lost
563  QgsPoint *tmp;
564  tmp = pHelperTop;
565  pHelperTop = pHelperBottom;
566  pHelperBottom = tmp;
567  calcHelper( mHelperTopRow + 2, pHelperBottom );
568  mHelperTopRow++;
569 }
570 
571 bool QgsRasterProjector::srcRowCol( int theDestRow, int theDestCol, int *theSrcRow, int *theSrcCol, const QgsCoordinateTransform* ct )
572 {
573  if ( mApproximate )
574  {
575  return approximateSrcRowCol( theDestRow, theDestCol, theSrcRow, theSrcCol );
576  }
577  else
578  {
579  return preciseSrcRowCol( theDestRow, theDestCol, theSrcRow, theSrcCol, ct );
580  }
581 }
582 
583 bool QgsRasterProjector::preciseSrcRowCol( int theDestRow, int theDestCol, int *theSrcRow, int *theSrcCol, const QgsCoordinateTransform* ct )
584 {
585 #ifdef QGISDEBUG
586  QgsDebugMsgLevel( QString( "theDestRow = %1" ).arg( theDestRow ), 5 );
587  QgsDebugMsgLevel( QString( "theDestRow = %1 mDestExtent.yMaximum() = %2 mDestYRes = %3" ).arg( theDestRow ).arg( mDestExtent.yMaximum() ).arg( mDestYRes ), 5 );
588 #endif
589 
590  // Get coordinate of center of destination cell
591  double x = mDestExtent.xMinimum() + ( theDestCol + 0.5 ) * mDestXRes;
592  double y = mDestExtent.yMaximum() - ( theDestRow + 0.5 ) * mDestYRes;
593  double z = 0;
594 
595 #ifdef QGISDEBUG
596  QgsDebugMsgLevel( QString( "x = %1 y = %2" ).arg( x ).arg( y ), 5 );
597 #endif
598 
599  if ( ct )
600  {
601  ct->transformInPlace( x, y, z );
602  }
603 
604 #ifdef QGISDEBUG
605  QgsDebugMsgLevel( QString( "x = %1 y = %2" ).arg( x ).arg( y ), 5 );
606 #endif
607 
608  if ( !mExtent.contains( QgsPoint( x, y ) ) )
609  {
610  return false;
611  }
612  // Get source row col
613  *theSrcRow = ( int ) floor(( mSrcExtent.yMaximum() - y ) / mSrcYRes );
614  *theSrcCol = ( int ) floor(( x - mSrcExtent.xMinimum() ) / mSrcXRes );
615 #ifdef QGISDEBUG
616  QgsDebugMsgLevel( QString( "mSrcExtent.yMinimum() = %1 mSrcExtent.yMaximum() = %2 mSrcYRes = %3" ).arg( mSrcExtent.yMinimum() ).arg( mSrcExtent.yMaximum() ).arg( mSrcYRes ), 5 );
617  QgsDebugMsgLevel( QString( "theSrcRow = %1 theSrcCol = %2" ).arg( *theSrcRow ).arg( *theSrcCol ), 5 );
618 #endif
619 
620  // With epsg 32661 (Polar Stereographic) it was happening that *theSrcCol == mSrcCols
621  // For now silently correct limits to avoid crashes
622  // TODO: review
623  // should not happen
624  if ( *theSrcRow >= mSrcRows ) return false;
625  if ( *theSrcRow < 0 ) return false;
626  if ( *theSrcCol >= mSrcCols ) return false;
627  if ( *theSrcCol < 0 ) return false;
628 
629  return true;
630 }
631 
632 bool QgsRasterProjector::approximateSrcRowCol( int theDestRow, int theDestCol, int *theSrcRow, int *theSrcCol )
633 {
634  int myMatrixRow = matrixRow( theDestRow );
635  int myMatrixCol = matrixCol( theDestCol );
636 
637  if ( myMatrixRow > mHelperTopRow )
638  {
639  // TODO: make it more robust (for random, not sequential reading)
640  nextHelper();
641  }
642 
643  double myDestY = mDestExtent.yMaximum() - ( theDestRow + 0.5 ) * mDestYRes;
644 
645  // See the schema in javax.media.jai.WarpGrid doc (but up side down)
646  // TODO: use some kind of cache of values which can be reused
647  double myDestXMin, myDestYMin, myDestXMax, myDestYMax;
648 
649  destPointOnCPMatrix( myMatrixRow + 1, myMatrixCol, &myDestXMin, &myDestYMin );
650  destPointOnCPMatrix( myMatrixRow, myMatrixCol + 1, &myDestXMax, &myDestYMax );
651 
652  double yfrac = ( myDestY - myDestYMin ) / ( myDestYMax - myDestYMin );
653 
654  QgsPoint &myTop = pHelperTop[theDestCol];
655  QgsPoint &myBot = pHelperBottom[theDestCol];
656 
657  // Warning: this is very SLOW compared to the following code!:
658  //double mySrcX = myBot.x() + (myTop.x() - myBot.x()) * yfrac;
659  //double mySrcY = myBot.y() + (myTop.y() - myBot.y()) * yfrac;
660 
661  double tx = myTop.x();
662  double ty = myTop.y();
663  double bx = myBot.x();
664  double by = myBot.y();
665  double mySrcX = bx + ( tx - bx ) * yfrac;
666  double mySrcY = by + ( ty - by ) * yfrac;
667 
668  if ( !mExtent.contains( QgsPoint( mySrcX, mySrcY ) ) )
669  {
670  return false;
671  }
672 
673  // TODO: check again cell selection (coor is in the middle)
674 
675  *theSrcRow = ( int ) floor(( mSrcExtent.yMaximum() - mySrcY ) / mSrcYRes );
676  *theSrcCol = ( int ) floor(( mySrcX - mSrcExtent.xMinimum() ) / mSrcXRes );
677 
678  // For now silently correct limits to avoid crashes
679  // TODO: review
680  // should not happen
681  if ( *theSrcRow >= mSrcRows ) return false;
682  if ( *theSrcRow < 0 ) return false;
683  if ( *theSrcCol >= mSrcCols ) return false;
684  if ( *theSrcCol < 0 ) return false;
685 
686  return true;
687 }
688 
689 void QgsRasterProjector::insertRows( const QgsCoordinateTransform* ct )
690 {
691  for ( int r = 0; r < mCPRows - 1; r++ )
692  {
693  QList<QgsPoint> myRow;
694  QList<bool> myLegalRow;
695  myRow.reserve( mCPCols );
696  myLegalRow.reserve( mCPCols );
697  for ( int c = 0; c < mCPCols; ++c )
698  {
699  myRow.append( QgsPoint() );
700  myLegalRow.append( false );
701  }
702  QgsDebugMsgLevel( QString( "insert new row at %1" ).arg( 1 + r*2 ), 3 );
703  mCPMatrix.insert( 1 + r*2, myRow );
704  mCPLegalMatrix.insert( 1 + r*2, myLegalRow );
705  }
706  mCPRows += mCPRows - 1;
707  for ( int r = 1; r < mCPRows - 1; r += 2 )
708  {
709  calcRow( r, ct );
710  }
711 }
712 
713 void QgsRasterProjector::insertCols( const QgsCoordinateTransform* ct )
714 {
715  for ( int r = 0; r < mCPRows; r++ )
716  {
717  QList<QgsPoint> myRow;
718  QList<bool> myLegalRow;
719  for ( int c = 0; c < mCPCols - 1; c++ )
720  {
721  mCPMatrix[r].insert( 1 + c*2, QgsPoint() );
722  mCPLegalMatrix[r].insert( 1 + c*2, false );
723  }
724  }
725  mCPCols += mCPCols - 1;
726  for ( int c = 1; c < mCPCols - 1; c += 2 )
727  {
728  calcCol( c, ct );
729  }
730 
731 }
732 
733 void QgsRasterProjector::calcCP( int theRow, int theCol, const QgsCoordinateTransform* ct )
734 {
735  double myDestX, myDestY;
736  destPointOnCPMatrix( theRow, theCol, &myDestX, &myDestY );
737  QgsPoint myDestPoint( myDestX, myDestY );
738  try
739  {
740  if ( ct )
741  {
742  mCPMatrix[theRow][theCol] = ct->transform( myDestPoint );
743  mCPLegalMatrix[theRow][theCol] = true;
744  }
745  else
746  {
747  mCPLegalMatrix[theRow][theCol] = false;
748  }
749  }
750  catch ( QgsCsException &e )
751  {
752  Q_UNUSED( e );
753  // Caught an error in transform
754  mCPLegalMatrix[theRow][theCol] = false;
755  }
756 }
757 
758 bool QgsRasterProjector::calcRow( int theRow, const QgsCoordinateTransform* ct )
759 {
760  QgsDebugMsgLevel( QString( "theRow = %1" ).arg( theRow ), 3 );
761  for ( int i = 0; i < mCPCols; i++ )
762  {
763  calcCP( theRow, i, ct );
764  }
765 
766  return true;
767 }
768 
769 bool QgsRasterProjector::calcCol( int theCol, const QgsCoordinateTransform* ct )
770 {
771  QgsDebugMsgLevel( QString( "theCol = %1" ).arg( theCol ), 3 );
772  for ( int i = 0; i < mCPRows; i++ )
773  {
774  calcCP( i, theCol, ct );
775  }
776 
777  return true;
778 }
779 
780 bool QgsRasterProjector::checkCols( const QgsCoordinateTransform* ct )
781 {
782  if ( !ct )
783  {
784  return false;
785  }
786 
787  for ( int c = 0; c < mCPCols; c++ )
788  {
789  for ( int r = 1; r < mCPRows - 1; r += 2 )
790  {
791  double myDestX, myDestY;
792  destPointOnCPMatrix( r, c, &myDestX, &myDestY );
793  QgsPoint myDestPoint( myDestX, myDestY );
794 
795  QgsPoint mySrcPoint1 = mCPMatrix[r-1][c];
796  QgsPoint mySrcPoint2 = mCPMatrix[r][c];
797  QgsPoint mySrcPoint3 = mCPMatrix[r+1][c];
798 
799  QgsPoint mySrcApprox(( mySrcPoint1.x() + mySrcPoint3.x() ) / 2, ( mySrcPoint1.y() + mySrcPoint3.y() ) / 2 );
800  if ( !mCPLegalMatrix[r-1][c] || !mCPLegalMatrix[r][c] || !mCPLegalMatrix[r+1][c] )
801  {
802  // There was an error earlier in transform, just abort
803  return false;
804  }
805  try
806  {
807  QgsPoint myDestApprox = ct->transform( mySrcApprox, QgsCoordinateTransform::ReverseTransform );
808  double mySqrDist = myDestApprox.sqrDist( myDestPoint );
809  if ( mySqrDist > mSqrTolerance )
810  {
811  return false;
812  }
813  }
814  catch ( QgsCsException &e )
815  {
816  Q_UNUSED( e );
817  // Caught an error in transform
818  return false;
819  }
820  }
821  }
822  return true;
823 }
824 
825 bool QgsRasterProjector::checkRows( const QgsCoordinateTransform* ct )
826 {
827  if ( !ct )
828  {
829  return false;
830  }
831 
832  for ( int r = 0; r < mCPRows; r++ )
833  {
834  for ( int c = 1; c < mCPCols - 1; c += 2 )
835  {
836  double myDestX, myDestY;
837  destPointOnCPMatrix( r, c, &myDestX, &myDestY );
838 
839  QgsPoint myDestPoint( myDestX, myDestY );
840  QgsPoint mySrcPoint1 = mCPMatrix[r][c-1];
841  QgsPoint mySrcPoint2 = mCPMatrix[r][c];
842  QgsPoint mySrcPoint3 = mCPMatrix[r][c+1];
843 
844  QgsPoint mySrcApprox(( mySrcPoint1.x() + mySrcPoint3.x() ) / 2, ( mySrcPoint1.y() + mySrcPoint3.y() ) / 2 );
845  if ( !mCPLegalMatrix[r][c-1] || !mCPLegalMatrix[r][c] || !mCPLegalMatrix[r][c+1] )
846  {
847  // There was an error earlier in transform, just abort
848  return false;
849  }
850  try
851  {
852  QgsPoint myDestApprox = ct->transform( mySrcApprox, QgsCoordinateTransform::ReverseTransform );
853  double mySqrDist = myDestApprox.sqrDist( myDestPoint );
854  if ( mySqrDist > mSqrTolerance )
855  {
856  return false;
857  }
858  }
859  catch ( QgsCsException &e )
860  {
861  Q_UNUSED( e );
862  // Caught an error in transform
863  return false;
864  }
865  }
866  }
867  return true;
868 }
869 
871 {
872  switch ( precision )
873  {
874  case Approximate:
875  return tr( "Approximate" );
876  case Exact:
877  return tr( "Exact" );
878  }
879  return "Unknown";
880 }
881 
882 QgsRasterBlock * QgsRasterProjector::block( int bandNo, QgsRectangle const & extent, int width, int height )
883 {
884  QgsDebugMsg( QString( "extent:\n%1" ).arg( extent.toString() ) );
885  QgsDebugMsg( QString( "width = %1 height = %2" ).arg( width ).arg( height ) );
886  if ( !mInput )
887  {
888  QgsDebugMsg( "Input not set" );
889  return new QgsRasterBlock();
890  }
891 
892  if ( ! mSrcCRS.isValid() || ! mDestCRS.isValid() || mSrcCRS == mDestCRS )
893  {
894  QgsDebugMsg( "No projection necessary" );
895  return mInput->block( bandNo, extent, width, height );
896  }
897 
898  mDestExtent = extent;
899  mDestRows = height;
900  mDestCols = width;
901  calc();
902 
903  QgsDebugMsg( QString( "srcExtent:\n%1" ).arg( srcExtent().toString() ) );
904  QgsDebugMsg( QString( "srcCols = %1 srcRows = %2" ).arg( srcCols() ).arg( srcRows() ) );
905 
906  // If we zoom out too much, projector srcRows / srcCols maybe 0, which can cause problems in providers
907  if ( srcRows() <= 0 || srcCols() <= 0 )
908  {
909  QgsDebugMsg( "Zero srcRows or srcCols" );
910  return new QgsRasterBlock();
911  }
912 
913  QgsRasterBlock *inputBlock = mInput->block( bandNo, srcExtent(), srcCols(), srcRows() );
914  if ( !inputBlock || inputBlock->isEmpty() )
915  {
916  QgsDebugMsg( "No raster data!" );
917  delete inputBlock;
918  return new QgsRasterBlock();
919  }
920 
921  qgssize pixelSize = QgsRasterBlock::typeSize( mInput->dataType( bandNo ) );
922 
923  QgsRasterBlock *outputBlock;
924  if ( inputBlock->hasNoDataValue() )
925  {
926  outputBlock = new QgsRasterBlock( inputBlock->dataType(), width, height, inputBlock->noDataValue() );
927  }
928  else
929  {
930  outputBlock = new QgsRasterBlock( inputBlock->dataType(), width, height );
931  }
932  if ( !outputBlock->isValid() )
933  {
934  QgsDebugMsg( "Cannot create block" );
935  delete inputBlock;
936  return outputBlock;
937  }
938 
939  // set output to no data, it should be fast
940  outputBlock->setIsNoData();
941 
942  // No data: because isNoData()/setIsNoData() is slow with respect to simple memcpy,
943  // we use if only if necessary:
944  // 1) no data value exists (numerical) -> memcpy, not necessary isNoData()/setIsNoData()
945  // 2) no data value does not exist but it may contain no data (numerical no data bitmap)
946  // -> must use isNoData()/setIsNoData()
947  // 3) no data are not used (no no data value, no no data bitmap) -> simple memcpy
948  // 4) image - simple memcpy
949 
950  // To copy no data values stored in bitmaps we have to use isNoData()/setIsNoData(),
951  // we cannot fill output block with no data because we use memcpy for data, not setValue().
952  bool doNoData = !QgsRasterBlock::typeIsNumeric( inputBlock->dataType() ) && inputBlock->hasNoData() && !inputBlock->hasNoDataValue();
953 
954  const QgsCoordinateTransform* inverseCt = 0;
955  if ( !mApproximate )
956  {
957  inverseCt = QgsCoordinateTransformCache::instance()->transform( mDestCRS.authid(), mSrcCRS.authid(), mDestDatumTransform, mSrcDatumTransform );
958  }
959 
960  outputBlock->setIsNoData();
961 
962  int srcRow, srcCol;
963  for ( int i = 0; i < height; ++i )
964  {
965  for ( int j = 0; j < width; ++j )
966  {
967  bool inside = srcRowCol( i, j, &srcRow, &srcCol, inverseCt );
968  if ( !inside ) continue; // we have everything set to no data
969 
970  qgssize srcIndex = ( qgssize )srcRow * mSrcCols + srcCol;
971  QgsDebugMsgLevel( QString( "row = %1 col = %2 srcRow = %3 srcCol = %4" ).arg( i ).arg( j ).arg( srcRow ).arg( srcCol ), 5 );
972 
973  // isNoData() may be slow so we check doNoData first
974  if ( doNoData && inputBlock->isNoData( srcRow, srcCol ) )
975  {
976  outputBlock->setIsNoData( i, j );
977  continue;
978  }
979 
980  qgssize destIndex = ( qgssize )i * width + j;
981  char *srcBits = inputBlock->bits( srcIndex );
982  char *destBits = outputBlock->bits( destIndex );
983  if ( !srcBits )
984  {
985  QgsDebugMsg( QString( "Cannot get input block data: row = %1 col = %2" ).arg( i ).arg( j ) );
986  continue;
987  }
988  if ( !destBits )
989  {
990  QgsDebugMsg( QString( "Cannot set output block data: srcRow = %1 srcCol = %2" ).arg( srcRow ).arg( srcCol ) );
991  continue;
992  }
993  memcpy( destBits, srcBits, pixelSize );
994  outputBlock->setIsData( i, j );
995  }
996  }
997 
998  delete inputBlock;
999 
1000  return outputBlock;
1001 }
1002 
1003 bool QgsRasterProjector::destExtentSize( const QgsRectangle& theSrcExtent, int theSrcXSize, int theSrcYSize,
1004  QgsRectangle& theDestExtent, int& theDestXSize, int& theDestYSize )
1005 {
1006  if ( theSrcExtent.isEmpty() || theSrcXSize <= 0 || theSrcYSize <= 0 )
1007  {
1008  return false;
1009  }
1010  const QgsCoordinateTransform* ct = QgsCoordinateTransformCache::instance()->transform( mSrcCRS.authid(), mDestCRS.authid(), mSrcDatumTransform, mDestDatumTransform );
1011 
1012  return extentSize( ct, theSrcExtent, theSrcXSize, theSrcYSize, theDestExtent, theDestXSize, theDestYSize );
1013 }
1014 
1016  const QgsRectangle& theSrcExtent, int theSrcXSize, int theSrcYSize,
1017  QgsRectangle& theDestExtent, int& theDestXSize, int& theDestYSize )
1018 {
1019  if ( theSrcExtent.isEmpty() || theSrcXSize <= 0 || theSrcYSize <= 0 )
1020  {
1021  return false;
1022  }
1023 
1024  theDestExtent = ct->transformBoundingBox( theSrcExtent );
1025 
1026  // We reproject pixel rectangle from 9 points matrix of source extent, of course, it gives
1027  // bigger xRes,yRes than reprojected edges (envelope)
1028  double srcXStep = theSrcExtent.width() / 3;
1029  double srcYStep = theSrcExtent.height() / 3;
1030  double srcXRes = theSrcExtent.width() / theSrcXSize;
1031  double srcYRes = theSrcExtent.height() / theSrcYSize;
1032  double destXRes = std::numeric_limits<double>::max();
1033  double destYRes = std::numeric_limits<double>::max();
1034 
1035  for ( int i = 0; i < 3; i++ )
1036  {
1037  double x = theSrcExtent.xMinimum() + i * srcXStep;
1038  for ( int j = 0; j < 3; j++ )
1039  {
1040  double y = theSrcExtent.yMinimum() + j * srcYStep;
1041  QgsRectangle srcRectangle( x - srcXRes / 2, y - srcYRes / 2, x + srcXRes / 2, y + srcYRes / 2 );
1042  QgsRectangle destRectangle = ct->transformBoundingBox( srcRectangle );
1043  if ( destRectangle.width() > 0 )
1044  {
1045  destXRes = std::min( destXRes, destRectangle.width() );
1046  }
1047  if ( destRectangle.height() > 0 )
1048  {
1049  destYRes = std::min( destYRes, destRectangle.height() );
1050  }
1051  }
1052  }
1053  theDestXSize = std::max( 1, ( int )( theDestExtent.width() / destYRes ) );
1054  theDestYSize = std::max( 1, ( int )( theDestExtent.height() / destYRes ) );
1055 
1056  return true;
1057 }
1058 
virtual int bandCount() const =0
Get number of bands.
void clear()
A rectangle specified with double values.
Definition: qgsrectangle.h:35
bool isEmpty() const
test if rectangle is empty.
void setCRS(const QgsCoordinateReferenceSystem &theSrcCRS, const QgsCoordinateReferenceSystem &theDestCRS, int srcDatumTransform=-1, int destDatumTransform=-1)
set source and destination CRS
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:171
Approximate (default), fast but possibly inaccurate.
double yMaximum() const
Get the y maximum value (top side of rectangle)
Definition: qgsrectangle.h:196
static bool typeIsNumeric(QGis::DataType type)
Returns true if data type is numeric.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
void reserve(int alloc)
bool contains(const QgsRectangle &rect) const
return true when rectangle contains other rectangle
virtual const QgsRasterInterface * srcInput() const
Get source / raw input, the first in pipe, usually provider.
double noDataValue() const
Return no data value.
QGis::DataType dataType() const
Returns data type.
QgsPoint transform(const QgsPoint &p, TransformDirection direction=ForwardTransform) const
Transform the point from Source Coordinate System to Destination Coordinate System If the direction i...
static QgsCoordinateTransformCache * instance()
Definition: qgscrscache.cpp:22
double sqrDist(double x, double y) const
Returns the squared distance between this point and x,y.
Definition: qgspoint.cpp:345
bool isNoData(int row, int column)
Check if value at position is no data.
double x() const
Get the x value of the point.
Definition: qgspoint.h:126
virtual int ySize() const
bool setIsNoData(int row, int column)
Set no data on pixel.
double ANALYSIS_EXPORT max(double x, double y)
Returns the maximum of two doubles or the first argument if both are equal.
const QgsCoordinateTransform * transform(const QString &srcAuthId, const QString &destAuthId, int srcDatumTransform=-1, int destDatumTransform=-1)
Returns coordinate transformation.
Definition: qgscrscache.cpp:37
void transformInPlace(double &x, double &y, double &z, TransformDirection direction=ForwardTransform) const
void combineExtentWith(QgsRectangle *rect)
expand the rectangle so that covers both the original rectangle and the given rectangle ...
void append(const T &value)
~QgsRasterProjector()
The destructor.
bool hasNoData() const
Returns true if the block may contain no data.
Raster data container.
double yMinimum() const
Get the y minimum value (bottom side of rectangle)
Definition: qgsrectangle.h:201
QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height) override
Read block of data using given extent and size.
double xMaximum() const
Get the x maximum value (right side of rectangle)
Definition: qgsrectangle.h:186
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:34
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:176
QString toString() const
String representation of the point (x,y)
Definition: qgspoint.cpp:126
static int typeSize(int dataType)
DataType
Raster data types.
Definition: qgis.h:122
virtual QGis::DataType dataType(int bandNo) const =0
Returns data type for the band specified by number.
QgsRasterInterface * clone() const override
Clone itself, create deep copy.
Base class for processing filters like renderers, reprojector, resampler etc.
A class to represent a point.
Definition: qgspoint.h:63
unsigned long long qgssize
Qgssize is used instead of size_t, because size_t is stdlib type, unknown by SIP, and it would be har...
Definition: qgis.h:375
void setX(double x)
Sets the x value of the point.
Definition: qgspoint.h:103
virtual int capabilities() const
Returns a bitmask containing the supported capabilities.
void setY(double y)
Sets the y value of the point.
Definition: qgspoint.h:111
virtual QgsRectangle extent() override=0
Get the extent of the data source.
virtual QgsRectangle extent()
Get the extent of the interface.
bool isValid() const
Find out whether this CRS is correctly initialised and usable.
bool hasNoDataValue() const
True if the block has no data value.
char * bits(int row, int column)
Get pointer to data.
Precision
Precison defines if each pixel is reprojected or approximate reprojection based on an approximation m...
virtual int xSize() const
Get raster size.
void insert(int i, const T &value)
Unknown or unspecified type.
Definition: qgis.h:124
void setYMaximum(double y)
Set the maximum y value.
Definition: qgsrectangle.h:181
QgsRectangle intersect(const QgsRectangle *rect) const
return the intersection with the given rectangle
Class for storing a coordinate reference system (CRS)
virtual QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height)=0
Read block of data using given extent and size.
QString authid() const
Get the authority identifier for this srs.
Class for doing transforms between two map coordinate systems.
static bool extentSize(const QgsCoordinateTransform *ct, const QgsRectangle &theSrcExtent, int theSrcXSize, int theSrcYSize, QgsRectangle &theDestExtent, int &theDestXSize, int &theDestYSize)
Calculate destination extent and size from source extent and size.
double y() const
Get the y value of the point.
Definition: qgspoint.h:134
Exact, precise but slow.
Custom exception class for Coordinate Reference System related exceptions.
QGis::DataType dataType(int bandNo) const override
Returns data type for the band specified by number.
double ANALYSIS_EXPORT min(double x, double y)
Returns the minimum of two doubles or the first argument if both are equal.
double width() const
Width of the rectangle.
Definition: qgsrectangle.h:206
bool destExtentSize(const QgsRectangle &theSrcExtent, int theSrcXSize, int theSrcYSize, QgsRectangle &theDestExtent, int &theDestXSize, int &theDestYSize)
Calculate destination extent and size from source extent and size.
QgsRasterInterface * mInput
QgsRasterProjector & operator=(const QgsRasterProjector &projector)
QString toString(bool automaticPrecision=false) const
returns string representation of form xmin,ymin xmax,ymax
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
double xMinimum() const
Get the x minimum value (left side of rectangle)
Definition: qgsrectangle.h:191
static QString precisionLabel(Precision precision)
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:166
QgsRectangle transformBoundingBox(const QgsRectangle &theRect, TransformDirection direction=ForwardTransform, const bool handle180Crossover=false) const
Transform a QgsRectangle to the dest Coordinate system If the direction is ForwardTransform then coor...
double height() const
Height of the rectangle.
Definition: qgsrectangle.h:211
bool isEmpty() const
Returns true if block is empty, i.e.
int bandCount() const override
Get number of bands.
Base class for raster data providers.
#define tr(sourceText)