QGIS API Documentation  3.0.2-Girona (307d082)
qgscoordinatetransform.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  QgsCoordinateTransform.cpp - Coordinate Transforms
3  -------------------
4  begin : Dec 2004
5  copyright : (C) 2004 Tim Sutton
6  email : tim at linfiniti.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 "qgscoordinatetransform.h"
19 #include "qgsapplication.h"
20 #include "qgsmessagelog.h"
21 #include "qgslogger.h"
22 #include "qgspointxy.h"
23 #include "qgsrectangle.h"
24 #include "qgsexception.h"
25 #include "qgsproject.h"
26 
27 //qt includes
28 #include <QDomNode>
29 #include <QDomElement>
30 #include <QApplication>
31 #include <QPolygonF>
32 #include <QStringList>
33 #include <QVector>
34 
35 extern "C"
36 {
37 #include <proj_api.h>
38 }
39 #include <sqlite3.h>
40 
41 // if defined shows all information about transform to stdout
42 // #define COORDINATE_TRANSFORM_VERBOSE
43 
44 QReadWriteLock QgsCoordinateTransform::sCacheLock;
45 QMultiHash< QPair< QString, QString >, QgsCoordinateTransform > QgsCoordinateTransform::sTransforms; //same auth_id pairs might have different datum transformations
46 
48 {
49  d = new QgsCoordinateTransformPrivate();
50 }
51 
53 {
54  d = new QgsCoordinateTransformPrivate( source, destination, QgsCoordinateTransformContext() );
55 
56  if ( !d->checkValidity() )
57  return;
58 
59  if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
60  {
61  d->initialize();
62  addToCache();
63  }
64 }
65 
67 {
68  mContext = context;
69  d = new QgsCoordinateTransformPrivate( source, destination, mContext );
70 #ifdef QGISDEBUG
71  mHasContext = true;
72 #endif
73 
74  if ( !d->checkValidity() )
75  return;
76 
77  if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
78  {
79  d->initialize();
80  addToCache();
81  }
82 }
83 
85 {
86  mContext = project ? project->transformContext() : QgsCoordinateTransformContext();
87  d = new QgsCoordinateTransformPrivate( source, destination, mContext );
88 #ifdef QGISDEBUG
89  if ( project )
90  mHasContext = true;
91 #endif
92 
93  if ( !d->checkValidity() )
94  return;
95 
96  if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
97  {
98  d->initialize();
99  addToCache();
100  }
101 }
102 
103 QgsCoordinateTransform::QgsCoordinateTransform( const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination, int sourceDatumTransform, int destinationDatumTransform )
104 {
105  d = new QgsCoordinateTransformPrivate( source, destination, sourceDatumTransform, destinationDatumTransform );
106 #ifdef QGISDEBUG
107  mHasContext = true; // not strictly true, but we don't need to worry if datums have been explicitly set
108 #endif
109 
110  if ( !d->checkValidity() )
111  return;
112 
113  if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
114  {
115  d->initialize();
116  addToCache();
117  }
118 }
119 
121  : mContext( o.mContext )
122 #ifdef QGISDEBUG
123  , mHasContext( o.mHasContext )
124 #endif
125 {
126  d = o.d;
127 }
128 
130 {
131  d = o.d;
132 #ifdef QGISDEBUG
133  mHasContext = o.mHasContext;
134 #endif
135  mContext = o.mContext;
136  return *this;
137 }
138 
140 
142 {
143  d.detach();
144  d->mSourceCRS = crs;
145  if ( !d->checkValidity() )
146  return;
147 
148  d->calculateTransforms( mContext );
149  if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
150  {
151  d->initialize();
152  addToCache();
153  }
154 }
156 {
157  d.detach();
158  d->mDestCRS = crs;
159  if ( !d->checkValidity() )
160  return;
161 
162  d->calculateTransforms( mContext );
163  if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
164  {
165  d->initialize();
166  addToCache();
167  }
168 }
169 
171 {
172  d.detach();
173  mContext = context;
174 #ifdef QGISDEBUG
175  mHasContext = true;
176 #endif
177  if ( !d->checkValidity() )
178  return;
179 
180  d->calculateTransforms( mContext );
181  if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
182  {
183  d->initialize();
184  addToCache();
185  }
186 }
187 
189 {
190  return d->mSourceCRS;
191 }
192 
194 {
195  return d->mDestCRS;
196 }
197 
199 {
200  if ( !d->mIsValid || d->mShortCircuit )
201  return point;
202 
203  // transform x
204  double x = point.x();
205  double y = point.y();
206  double z = 0.0;
207  try
208  {
209  transformCoords( 1, &x, &y, &z, direction );
210  }
211  catch ( const QgsCsException & )
212  {
213  // rethrow the exception
214  QgsDebugMsg( "rethrowing exception" );
215  throw;
216  }
217 
218  return QgsPointXY( x, y );
219 }
220 
221 
222 QgsPointXY QgsCoordinateTransform::transform( const double theX, const double theY = 0.0, TransformDirection direction ) const
223 {
224  try
225  {
226  return transform( QgsPointXY( theX, theY ), direction );
227  }
228  catch ( const QgsCsException & )
229  {
230  // rethrow the exception
231  QgsDebugMsg( "rethrowing exception" );
232  throw;
233  }
234 }
235 
237 {
238  if ( !d->mIsValid || d->mShortCircuit )
239  return rect;
240  // transform x
241  double x1 = rect.xMinimum();
242  double y1 = rect.yMinimum();
243  double x2 = rect.xMaximum();
244  double y2 = rect.yMaximum();
245 
246  // Number of points to reproject------+
247  // |
248  // V
249  try
250  {
251  double z = 0.0;
252  transformCoords( 1, &x1, &y1, &z, direction );
253  transformCoords( 1, &x2, &y2, &z, direction );
254  }
255  catch ( const QgsCsException & )
256  {
257  // rethrow the exception
258  QgsDebugMsg( "rethrowing exception" );
259  throw;
260  }
261 
262 #ifdef COORDINATE_TRANSFORM_VERBOSE
263  QgsDebugMsg( "Rect projection..." );
264  QgsDebugMsg( QString( "Xmin : %1 --> %2" ).arg( rect.xMinimum() ).arg( x1 ) );
265  QgsDebugMsg( QString( "Ymin : %1 --> %2" ).arg( rect.yMinimum() ).arg( y1 ) );
266  QgsDebugMsg( QString( "Xmax : %1 --> %2" ).arg( rect.xMaximum() ).arg( x2 ) );
267  QgsDebugMsg( QString( "Ymax : %1 --> %2" ).arg( rect.yMaximum() ).arg( y2 ) );
268 #endif
269  return QgsRectangle( x1, y1, x2, y2 );
270 }
271 
272 void QgsCoordinateTransform::transformInPlace( double &x, double &y, double &z,
273  TransformDirection direction ) const
274 {
275  if ( !d->mIsValid || d->mShortCircuit )
276  return;
277 #ifdef QGISDEBUG
278 // QgsDebugMsg(QString("Using transform in place %1 %2").arg(__FILE__).arg(__LINE__));
279 #endif
280  // transform x
281  try
282  {
283  transformCoords( 1, &x, &y, &z, direction );
284  }
285  catch ( const QgsCsException & )
286  {
287  // rethrow the exception
288  QgsDebugMsg( "rethrowing exception" );
289  throw;
290  }
291 }
292 
293 void QgsCoordinateTransform::transformInPlace( float &x, float &y, double &z,
294  TransformDirection direction ) const
295 {
296  double xd = static_cast< double >( x ), yd = static_cast< double >( y );
297  transformInPlace( xd, yd, z, direction );
298  x = xd;
299  y = yd;
300 }
301 
302 void QgsCoordinateTransform::transformInPlace( float &x, float &y, float &z,
303  TransformDirection direction ) const
304 {
305  if ( !d->mIsValid || d->mShortCircuit )
306  return;
307 #ifdef QGISDEBUG
308  // QgsDebugMsg(QString("Using transform in place %1 %2").arg(__FILE__).arg(__LINE__));
309 #endif
310  // transform x
311  try
312  {
313  double xd = x;
314  double yd = y;
315  double zd = z;
316  transformCoords( 1, &xd, &yd, &zd, direction );
317  x = xd;
318  y = yd;
319  z = zd;
320  }
321  catch ( QgsCsException & )
322  {
323  // rethrow the exception
324  QgsDebugMsg( "rethrowing exception" );
325  throw;
326  }
327 }
328 
329 void QgsCoordinateTransform::transformPolygon( QPolygonF &poly, TransformDirection direction ) const
330 {
331  if ( !d->mIsValid || d->mShortCircuit )
332  {
333  return;
334  }
335 
336  //create x, y arrays
337  int nVertices = poly.size();
338 
339  QVector<double> x( nVertices );
340  QVector<double> y( nVertices );
341  QVector<double> z( nVertices );
342 
343  for ( int i = 0; i < nVertices; ++i )
344  {
345  const QPointF &pt = poly.at( i );
346  x[i] = pt.x();
347  y[i] = pt.y();
348  z[i] = 0;
349  }
350 
351  try
352  {
353  transformCoords( nVertices, x.data(), y.data(), z.data(), direction );
354  }
355  catch ( const QgsCsException & )
356  {
357  // rethrow the exception
358  QgsDebugMsg( "rethrowing exception" );
359  throw;
360  }
361 
362  for ( int i = 0; i < nVertices; ++i )
363  {
364  QPointF &pt = poly[i];
365  pt.rx() = x[i];
366  pt.ry() = y[i];
367  }
368 }
369 
371  QVector<double> &x, QVector<double> &y, QVector<double> &z,
372  TransformDirection direction ) const
373 {
374 
375  if ( !d->mIsValid || d->mShortCircuit )
376  return;
377 
378  Q_ASSERT( x.size() == y.size() );
379 
380  // Apparently, if one has a std::vector, it is valid to use the
381  // address of the first element in the vector as a pointer to an
382  // array of the vectors data, and hence easily interface with code
383  // that wants C-style arrays.
384 
385  try
386  {
387  transformCoords( x.size(), &x[0], &y[0], &z[0], direction );
388  }
389  catch ( const QgsCsException & )
390  {
391  // rethrow the exception
392  QgsDebugMsg( "rethrowing exception" );
393  throw;
394  }
395 }
396 
397 
399  QVector<float> &x, QVector<float> &y, QVector<float> &z,
400  TransformDirection direction ) const
401 {
402  if ( !d->mIsValid || d->mShortCircuit )
403  return;
404 
405  Q_ASSERT( x.size() == y.size() );
406 
407  // Apparently, if one has a std::vector, it is valid to use the
408  // address of the first element in the vector as a pointer to an
409  // array of the vectors data, and hence easily interface with code
410  // that wants C-style arrays.
411 
412  try
413  {
414  //copy everything to double vectors since proj needs double
415  int vectorSize = x.size();
416  QVector<double> xd( x.size() );
417  QVector<double> yd( y.size() );
418  QVector<double> zd( z.size() );
419  for ( int i = 0; i < vectorSize; ++i )
420  {
421  xd[i] = x[i];
422  yd[i] = y[i];
423  zd[i] = z[i];
424  }
425  transformCoords( x.size(), &xd[0], &yd[0], &zd[0], direction );
426 
427  //copy back
428  for ( int i = 0; i < vectorSize; ++i )
429  {
430  x[i] = xd[i];
431  y[i] = yd[i];
432  z[i] = zd[i];
433  }
434  }
435  catch ( QgsCsException & )
436  {
437  // rethrow the exception
438  QgsDebugMsg( "rethrowing exception" );
439  throw;
440  }
441 }
442 
443 QgsRectangle QgsCoordinateTransform::transformBoundingBox( const QgsRectangle &rect, TransformDirection direction, const bool handle180Crossover ) const
444 {
445  // Calculate the bounding box of a QgsRectangle in the source CRS
446  // when projected to the destination CRS (or the inverse).
447  // This is done by looking at a number of points spread evenly
448  // across the rectangle
449 
450  if ( !d->mIsValid || d->mShortCircuit )
451  return rect;
452 
453  if ( rect.isEmpty() )
454  {
455  QgsPointXY p = transform( rect.xMinimum(), rect.yMinimum(), direction );
456  return QgsRectangle( p, p );
457  }
458 
459  // 64 points (<=2.12) is not enough, see #13665, for EPSG:4326 -> EPSG:3574 (say that it is a hard one),
460  // are decent result from about 500 points and more. This method is called quite often, but
461  // even with 1000 points it takes < 1ms
462  // TODO: how to effectively and precisely reproject bounding box?
463  const int nPoints = 1000;
464  double d = std::sqrt( ( rect.width() * rect.height() ) / std::pow( std::sqrt( static_cast< double >( nPoints ) ) - 1, 2.0 ) );
465  int nXPoints = static_cast< int >( std::ceil( rect.width() / d ) ) + 1;
466  int nYPoints = static_cast< int >( std::ceil( rect.height() / d ) ) + 1;
467 
468  QgsRectangle bb_rect;
469  bb_rect.setMinimal();
470 
471  // We're interfacing with C-style vectors in the
472  // end, so let's do C-style vectors here too.
473 
474  QVector<double> x( nXPoints * nYPoints );
475  QVector<double> y( nXPoints * nYPoints );
476  QVector<double> z( nXPoints * nYPoints );
477 
478  QgsDebugMsgLevel( "Entering transformBoundingBox...", 4 );
479 
480  // Populate the vectors
481 
482  double dx = rect.width() / static_cast< double >( nXPoints - 1 );
483  double dy = rect.height() / static_cast< double >( nYPoints - 1 );
484 
485  double pointY = rect.yMinimum();
486 
487  for ( int i = 0; i < nYPoints ; i++ )
488  {
489 
490  // Start at right edge
491  double pointX = rect.xMinimum();
492 
493  for ( int j = 0; j < nXPoints; j++ )
494  {
495  x[( i * nXPoints ) + j] = pointX;
496  y[( i * nXPoints ) + j] = pointY;
497  // and the height...
498  z[( i * nXPoints ) + j] = 0.0;
499  // QgsDebugMsg(QString("BBox coord: (%1, %2)").arg(x[(i*numP) + j]).arg(y[(i*numP) + j]));
500  pointX += dx;
501  }
502  pointY += dy;
503  }
504 
505  // Do transformation. Any exception generated must
506  // be handled in above layers.
507  try
508  {
509  transformCoords( nXPoints * nYPoints, x.data(), y.data(), z.data(), direction );
510  }
511  catch ( const QgsCsException & )
512  {
513  // rethrow the exception
514  QgsDebugMsg( "rethrowing exception" );
515  throw;
516  }
517 
518  // Calculate the bounding box and use that for the extent
519 
520  for ( int i = 0; i < nXPoints * nYPoints; i++ )
521  {
522  if ( !std::isfinite( x[i] ) || !std::isfinite( y[i] ) )
523  {
524  continue;
525  }
526 
527  if ( handle180Crossover )
528  {
529  //if crossing the date line, temporarily add 360 degrees to -ve longitudes
530  bb_rect.combineExtentWith( x[i] >= 0.0 ? x[i] : x[i] + 360.0, y[i] );
531  }
532  else
533  {
534  bb_rect.combineExtentWith( x[i], y[i] );
535  }
536  }
537 
538  if ( bb_rect.isNull() )
539  {
540  // something bad happened when reprojecting the filter rect... no finite points were left!
541  throw QgsCsException( QObject::tr( "Could not transform bounding box to target CRS" ) );
542  }
543 
544  if ( handle180Crossover )
545  {
546  //subtract temporary addition of 360 degrees from longitudes
547  if ( bb_rect.xMinimum() > 180.0 )
548  bb_rect.setXMinimum( bb_rect.xMinimum() - 360.0 );
549  if ( bb_rect.xMaximum() > 180.0 )
550  bb_rect.setXMaximum( bb_rect.xMaximum() - 360.0 );
551  }
552 
553  QgsDebugMsgLevel( "Projected extent: " + bb_rect.toString(), 4 );
554 
555  if ( bb_rect.isEmpty() )
556  {
557  QgsDebugMsgLevel( "Original extent: " + rect.toString(), 4 );
558  }
559 
560  return bb_rect;
561 }
562 
563 void QgsCoordinateTransform::transformCoords( int numPoints, double *x, double *y, double *z, TransformDirection direction ) const
564 {
565  if ( !d->mIsValid || d->mShortCircuit )
566  return;
567  // Refuse to transform the points if the srs's are invalid
568  if ( !d->mSourceCRS.isValid() )
569  {
570  QgsMessageLog::logMessage( QObject::tr( "The source spatial reference system (CRS) is not valid. "
571  "The coordinates can not be reprojected. The CRS is: %1" )
572  .arg( d->mSourceCRS.toProj4() ), QObject::tr( "CRS" ) );
573  return;
574  }
575  if ( !d->mDestCRS.isValid() )
576  {
577  QgsMessageLog::logMessage( QObject::tr( "The destination spatial reference system (CRS) is not valid. "
578  "The coordinates can not be reprojected. The CRS is: %1" ).arg( d->mDestCRS.toProj4() ), QObject::tr( "CRS" ) );
579  return;
580  }
581 
582 #ifdef COORDINATE_TRANSFORM_VERBOSE
583  double xorg = *x;
584  double yorg = *y;
585  QgsDebugMsg( QString( "[[[[[[ Number of points to transform: %1 ]]]]]]" ).arg( numPoints ) );
586 #endif
587 
588 #ifdef QGISDEBUG
589  if ( !mHasContext )
590  qWarning( "No QgsCoordinateTransformContext context set for transform" );
591 #endif
592 
593  // use proj4 to do the transform
594 
595  // if the source/destination projection is lat/long, convert the points to radians
596  // prior to transforming
597  QPair<projPJ, projPJ> projData = d->threadLocalProjData();
598  projPJ sourceProj = projData.first;
599  projPJ destProj = projData.second;
600 
601  if ( ( pj_is_latlong( destProj ) && ( direction == ReverseTransform ) )
602  || ( pj_is_latlong( sourceProj ) && ( direction == ForwardTransform ) ) )
603  {
604  for ( int i = 0; i < numPoints; ++i )
605  {
606  x[i] *= DEG_TO_RAD;
607  y[i] *= DEG_TO_RAD;
608  }
609 
610  }
611  int projResult;
612  if ( direction == ReverseTransform )
613  {
614  projResult = pj_transform( destProj, sourceProj, numPoints, 0, x, y, z );
615  }
616  else
617  {
618  Q_ASSERT( sourceProj );
619  Q_ASSERT( destProj );
620  projResult = pj_transform( sourceProj, destProj, numPoints, 0, x, y, z );
621  }
622 
623  if ( projResult != 0 )
624  {
625  //something bad happened....
626  QString points;
627 
628  for ( int i = 0; i < numPoints; ++i )
629  {
630  if ( direction == ForwardTransform )
631  {
632  points += QStringLiteral( "(%1, %2)\n" ).arg( x[i], 0, 'f' ).arg( y[i], 0, 'f' );
633  }
634  else
635  {
636  points += QStringLiteral( "(%1, %2)\n" ).arg( x[i] * RAD_TO_DEG, 0, 'f' ).arg( y[i] * RAD_TO_DEG, 0, 'f' );
637  }
638  }
639 
640  QString dir = ( direction == ForwardTransform ) ? QObject::tr( "forward transform" ) : QObject::tr( "inverse transform" );
641 
642  char *srcdef = pj_get_def( sourceProj, 0 );
643  char *dstdef = pj_get_def( destProj, 0 );
644 
645  QString msg = QObject::tr( "%1 of\n"
646  "%2"
647  "PROJ: %3 +to %4\n"
648  "Error: %5" )
649  .arg( dir,
650  points,
651  srcdef, dstdef,
652  QString::fromUtf8( pj_strerrno( projResult ) ) );
653 
654  pj_dalloc( srcdef );
655  pj_dalloc( dstdef );
656 
657  QgsDebugMsg( "Projection failed emitting invalid transform signal: " + msg );
658  QgsDebugMsg( "throwing exception" );
659 
660  throw QgsCsException( msg );
661  }
662 
663  // if the result is lat/long, convert the results from radians back
664  // to degrees
665  if ( ( pj_is_latlong( destProj ) && ( direction == ForwardTransform ) )
666  || ( pj_is_latlong( sourceProj ) && ( direction == ReverseTransform ) ) )
667  {
668  for ( int i = 0; i < numPoints; ++i )
669  {
670  x[i] *= RAD_TO_DEG;
671  y[i] *= RAD_TO_DEG;
672  }
673  }
674 #ifdef COORDINATE_TRANSFORM_VERBOSE
675  QgsDebugMsg( QString( "[[[[[[ Projected %1, %2 to %3, %4 ]]]]]]" )
676  .arg( xorg, 0, 'g', 15 ).arg( yorg, 0, 'g', 15 )
677  .arg( *x, 0, 'g', 15 ).arg( *y, 0, 'g', 15 ) );
678 #endif
679 }
680 
682 {
683  return d->mIsValid;
684 }
685 
687 {
688  return !d->mIsValid || d->mShortCircuit;
689 }
690 
691 const char *finder( const char *name )
692 {
693  QString proj;
694 #ifdef Q_OS_WIN
695  proj = QApplication::applicationDirPath()
696  + "/share/proj/" + QString( name );
697 #else
698  Q_UNUSED( name );
699 #endif
700  return proj.toUtf8();
701 }
702 
703 bool QgsCoordinateTransform::setFromCache( const QgsCoordinateReferenceSystem &src, const QgsCoordinateReferenceSystem &dest, int srcDatumTransform, int destDatumTransform )
704 {
705  if ( !src.isValid() || !dest.isValid() )
706  return false;
707 
708  sCacheLock.lockForRead();
709  const QList< QgsCoordinateTransform > values = sTransforms.values( qMakePair( src.authid(), dest.authid() ) );
710  for ( auto valIt = values.constBegin(); valIt != values.constEnd(); ++valIt )
711  {
712  if ( ( *valIt ).sourceDatumTransformId() == srcDatumTransform &&
713  ( *valIt ).destinationDatumTransformId() == destDatumTransform )
714  {
715  // need to save, and then restore the context... we don't want this to be cached or to use the values from the cache
716  QgsCoordinateTransformContext context = mContext;
717 #ifdef QGISDEBUG
718  bool hasContext = mHasContext;
719 #endif
720  *this = *valIt;
721  sCacheLock.unlock();
722 
723  mContext = context;
724 #ifdef QGISDEBUG
725  mHasContext = hasContext;
726 #endif
727 
728  return true;
729  }
730  }
731  sCacheLock.unlock();
732  return false;
733 }
734 
735 void QgsCoordinateTransform::addToCache()
736 {
737  if ( !d->mSourceCRS.isValid() || !d->mDestCRS.isValid() )
738  return;
739 
740  sCacheLock.lockForWrite();
741  sTransforms.insertMulti( qMakePair( d->mSourceCRS.authid(), d->mDestCRS.authid() ), *this );
742  sCacheLock.unlock();
743 }
744 
746 {
747  return d->mSourceDatumTransform;
748 }
749 
751 {
752  d.detach();
753  d->mSourceDatumTransform = dt;
754 }
755 
757 {
758  return d->mDestinationDatumTransform;
759 }
760 
762 {
763  d.detach();
764  d->mDestinationDatumTransform = dt;
765 }
766 
768 {
769  sCacheLock.lockForWrite();
770  sTransforms.clear();
771  sCacheLock.unlock();
772 }
void transformCoords(int numPoint, double *x, double *y, double *z, TransformDirection direction=ForwardTransform) const
Transform an array of coordinates to the destination CRS.
A rectangle specified with double values.
Definition: qgsrectangle.h:39
void setDestinationDatumTransformId(int datumId)
Sets the datumId ID of the datum transform to use when projecting to the destination CRS...
void setMinimal()
Set a rectangle so that min corner is at max and max corner is at min.
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:94
void setContext(const QgsCoordinateTransformContext &context)
Sets the context in which the coordinate transform should be calculated.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
double y
Definition: qgspointxy.h:48
A class to represent a 2D point.
Definition: qgspointxy.h:43
TransformDirection
Enum used to indicate the direction (forward or inverse) of the transform.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, TransformDirection direction=ForwardTransform, const bool handle180Crossover=false) const
Transforms a rectangle from the source CRS to the destination CRS.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system, which the transform will transform coordinates t...
QgsCoordinateTransform & operator=(const QgsCoordinateTransform &o)
Assignment operator.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning)
add a message to the instance (and create it if necessary)
QgsCoordinateTransformContext transformContext() const
Returns a copy of the project&#39;s coordinate transform context, which stores various information regard...
Definition: qgsproject.cpp:474
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
QgsCoordinateTransform()
Default constructor, creates an invalid QgsCoordinateTransform.
QgsCoordinateReferenceSystem sourceCrs() const
Returns the source coordinate reference system, which the transform will transform coordinates from...
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Sets the destination coordinate reference system.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
bool isEmpty() const
Returns true if the rectangle is empty.
bool isShortCircuited() const
Returns true if the transform short circuits because the source and destination are equivalent...
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:142
QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
void transformPolygon(QPolygonF &polygon, TransformDirection direction=ForwardTransform) const
Transforms a polygon to the destination coordinate system.
Reads and writes project states.
Definition: qgsproject.h:82
Contains information about the context in which a coordinate transform is executed.
int destinationDatumTransformId() const
Returns the ID of the datum transform to use when projecting to the destination CRS.
double x
Definition: qgspointxy.h:47
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:130
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:115
void setSourceDatumTransformId(int datumId)
Sets the datumId ID of the datum transform to use when projecting from the source CRS...
void combineExtentWith(const QgsRectangle &rect)
Expand the rectangle so that covers both the original rectangle and the given rectangle.
Transform from destination to source CRS.
QgsPointXY transform(const QgsPointXY &point, TransformDirection direction=ForwardTransform) const
Transform the point from the source CRS to the destination CRS.
static void invalidateCache()
Clears the internal cache used to initialize QgsCoordinateTransform objects.
This class represents a coordinate reference system (CRS).
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Class for doing transforms between two map coordinate systems.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:120
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:125
Transform from source to destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
int sourceDatumTransformId() const
Returns the ID of the datum transform to use when projecting from the source CRS. ...
void transformInPlace(double &x, double &y, double &z, TransformDirection direction=ForwardTransform) const
Transforms an array of x, y and z double coordinates in place, from the source CRS to the destination...
void setSourceCrs(const QgsCoordinateReferenceSystem &crs)
Sets the source coordinate reference system.
QString authid() const
Returns the authority identifier for the CRS.
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:89
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:149
const char * finder(const char *name)
bool isValid() const
Returns whether this CRS is correctly initialized and usable.